CoreGraphicsコードをSwift 3.0に変換する

Xcode 8.0が登場しました。
旧プロジェクトをXcode 8.0で開くと、Swift 3.0(あるいはSwift 2.4)に変換するよう促すダイアログが出現しました。

CoreGraphicsコードをSwift 3.0への自動コンバートにかけたところ、変換されない部分が残りました。

        let pathRef = CGMutablePath()
        CGPathMoveToPoint(pathRef, nil, 20, 0)
        CGPathAddLineToPoint(pathRef, nil, 200, 0)
        CGPathAddCurveToPoint(pathRef, nil, 205.523, 0, 210, 4.435, 210, 10)
        CGPathAddLineToPoint(pathRef, nil, 210, 122)
        CGPathAddCurveToPoint(pathRef, nil, 210, 127.565, 205.523, 132, 200, 132)
        CGPathAddLineToPoint(pathRef, nil, 10, 132)
        CGPathAddCurveToPoint(pathRef, nil, 4.477, 132, -0, 127.565, -0, 122)
        CGPathAddLineToPoint(pathRef, nil, -0, 20)
        CGPathAddCurveToPoint(pathRef, nil, -0, 9.087, 9.081, 0, 20, 0)
        pathRef.closeSubpath()

変換後、このようなコードになったのですが最終的には

        let pathRef = CGMutablePath()
        pathRef.move(to: CGPoint(x: 20, y: 0))
        pathRef.addLine(to: CGPoint(x: 200, y: 0))
        pathRef.addCurve(to: CGPoint(x: 210, y: 10), control1: CGPoint(x: 205.523, y: 0), control2: CGPoint(x: 210, y: 4.435))
        pathRef.addLine(to: CGPoint(x: 210, y: 122))
        pathRef.addCurve(to: CGPoint(x: 200, y: 132), control1: CGPoint(x: 210, y: 127.565), control2: CGPoint(x: 205.523, y: 132))
        pathRef.addLine(to: CGPoint(x: 10, y: 132))
        pathRef.addCurve(to: CGPoint(x: -0, y: 122), control1: CGPoint(x: 4.477, y: 132), control2: CGPoint(x: -0, y: 127.565))
        pathRef.addLine(to: CGPoint(x: -0, y: 20))
        pathRef.addCurve(to: CGPoint(x: 20, y: 0), control1: CGPoint(x: -0, y: 9.087), control2: CGPoint(x: 9.081, y: 0))
        pathRef.closeSubpath()

と変換したい。

文字列の処理がやりやすいのはなにかしらと考えて、急遽Rubyを学ぶことにしました。


文字列操作とファイル操作の基本的なやりかたを知ったので、以下の変換プログラム converter.rbを書きました。
CGPathMoveToPoint(pathRef, nil, 20, 0)

pathRef.move(to: CGPoint(x: 20, y: 0))

CGPathAddLineToPoint(pathRef, nil, 200, 0)

pathRef.addLine(to: CGPoint(x: 200, y: 0))

CGPathAddCurveToPoint(pathRef, nil, 205.523, 0, 210, 4.435, 210, 10)

pathRef.addCurve(to: CGPoint(x: 210, y: 10), control1: CGPoint(x: 205.523, y: 0), control2: CGPoint(x: 210, y: 4.435))

と変換するものです。

converter.rb

class Converter

    def convert()
        loop do 
            print "Filename? "
            fileName = gets.chomp
            
            if fileName == ""
                break
            end
            
            # Backup
            from = fileName
            to = "_" + fileName + ".bak"
            copy(from, to)
            
            # Conversion
            array = []
            File.open(fileName) do |file|
                file.each_line do |line|
                    line = convertCoreGraphicsCode(line)
                    array.push(line)
                end
            end
                
            # Writing
            File.open(fileName, "w") do |file|
                file.puts(array)
                puts fileName + " 変換終了"
            end
        end
    end
    
    def copy(from, to)
        File.open(from) do |input|
            File.open(to, "w") do |output|
                output.write(input.read)
            end
        end
    end

    def convertCoreGraphicsCode(line)
        if line.include?("CGPathMoveToPoint")
            return convertMoveToPoint(line)
        elsif line.include?("CGPathAddLineToPoint") 
            return convertAddLineToPoint(line)
        elsif line.include?("CGPathAddCurveToPoint")
            return convertAddCurveToPoint(line)
        else
            return line
        end
    end
    
    # CGPathMoveToPoint(clipPath, nil, 240, 122)
    # to
    # clipPath.move(to: CGPoint(x: 240, y: 122))
    def convertMoveToPoint(line)
        # indentを取得
        index = line.index("CGPathMoveToPoint")
        indent = line[0, index]
        
        # path名を含むかたまりを取得("CGPathMoveToPoint(clipPath,")
        pathStr = line.match(/CGPathMoveToPoint\(\w+,/)
        
        # path名を取得("clipPath")
        pathName = pathStr[0].sub("CGPathMoveToPoint\(", "").chop
        # puts pathName
        
        # 数値
        figuresStr = line.sub(/CGPathMoveToPoint\(\w+, nil,/, "").chomp.chop.lstrip
        figuresStr = " " + figuresStr
        figures = figuresStr.split(",")
        # puts figures
        
        dstStr = "%s%s.move(to: CGPoint(x:%s, y:%s))" % [indent, pathName, figures[0], figures[1]]
        # puts dstStr
        return dstStr
    end
    
    
    # CGPathAddLineToPoint(clipPath, nil, 240, 122)
    # to
    # clipPath.addLine(to: CGPoint(x: 240, y: 122))
    def convertAddLineToPoint(line)
        # indentを取得
        index = line.index("CGPathAddLineToPoint")
        indent = line[0, index]
        
        # path名を含むかたまりを取得("CGPathAddLineToPoint(clipPath,")
        pathStr = line.match(/CGPathAddLineToPoint\(\w+,/)
        
        # path名を取得("clipPath")
        pathName = pathStr[0].sub("CGPathAddLineToPoint\(", "").chop
        # puts pathName
        
        # 数値
        figuresStr = line.sub(/CGPathAddLineToPoint\(\w+, nil,/, "").chomp.chop.lstrip
        figuresStr = " " + figuresStr
        figures = figuresStr.split(",")
        # puts figures

        dstStr = "%s%s.addLine(to: CGPoint(x:%s, y:%s))" % [indent, pathName, figures[0], figures[1]]
        # puts dstStr
        return dstStr
    end
    
    # CGPathAddCurveToPoint(pathRef2, nil, 4.477, 132, 0, 127.565, 0, 122)
    # to
    # pathRef2.addCurve(to: CGPoint(x: 0, y: 122), control1: CGPoint(x: 4.477, y: 132), control2: CGPoint(x: 0, y: 127.565))
    def convertAddCurveToPoint(line)

        # indentを取得
        index = line.index("CGPathAddCurveToPoint")
        indent = line[0, index]
        
        # path名を含むかたまりを取得("CGPathAddCurveToPoint(pathRef2,")
        pathStr = line.match(/CGPathAddCurveToPoint\(\w+,/)
        
        # path名を取得("pathRef2")
        pathName = pathStr[0].sub("CGPathAddCurveToPoint\(", "").chop
        # puts pathName
        
        # 数値を取得
        figuresStr = line.sub(/CGPathAddCurveToPoint\(\w+, nil,/, "").chomp.chop.lstrip
        figuresStr = " " + figuresStr
        figures = figuresStr.split(",")
        # puts figures
        
        dstStr = "%s%s.addCurve(to: CGPoint(x:%s, y:%s), control1: CGPoint(x:%s, y:%s), control2: CGPoint(x:%s, y:%s))" % [indent, pathName, figures[4], figures[5], figures[0], figures[1], figures[2], figures[3]]
        # puts dstStr
        return dstStr 
    end

end

converter = Converter.new
converter.convert()

変換したいswiftファイルがあるフォルダに、converter.rbをおき、Terminalでそのフォルダへ移動した後、

ruby converter.rb  

と入力するとプログラムが実行されます。
実行すると、

FileName?  

とたずねられるので、そこで変換したいファイル名を入力すると変換が行われます。
(このさい、”_元ファイル名.bak”というバックアップファイルも作成します。)
ファイル名を入力せずにreturnキーを押すと、プログラムは終了します。

関連

Autodesk Graphic(旧iDraw)はCore Graphicsのコードを生成できる – nackpan Blog

[Xcode]Playgroundに画像ファイルを加える(Xcode 7.1)

以前、[Xcode]Playgroundに画像ファイルを加える | nackpan Blogという記事を書いたけれども、Xcode 7.1になって、画像を直接Source Editor(Playground開始して最初に開いているプログラミングを書き込んでいく部分)にドラッグすることができるようになっていました。

Adding Image Literals

Source Editorにドラッグすると、小さなサムネイル状になります。イメージリテラルと呼ぶそうです。
(Navigator(左ペイン)を開くと、Resourcesに画像ファイルが加わっています)
ss 2015-10-28 23.10.22
小さすぎて、いくつか画像があると区別がつかない気もします。

ss 2015-10-28 23.28.39

ss 2015-10-28 23.13.28

[Xcode]Playgroundで新たなページを作成する

Playgroundを使って、Swiftのチュートリアルをやっていた。
Swift Functional Programming Tutorial – Ray Wenderlich
2014年9月の記事なので、Swiftのバージョンは1.x系でXcodeは6の頃。

ひとまとまり終わった後、では新たにPlaygroundを作成してください。と指示がある。
新たにPlaygroundを作成せずとも、確かXcode 7でのPlaygroundでは新たなpageを加える機能があったはず。
New Features in Xcode 7
Adding Pages

これを使おう。


ss 2015-10-25 21.58.04
Navigatorペイン(左側ペイン)を表示する。

ss 2015-10-25 21.58.30
左下の「+」ボタンをクリックして、”New Page”を選択。
これで新たなページが追加される。

(もしくは、メニューから、File > New > Playground Pageを選ぶ)

[Xcode]プロジェクト名を変更する

開発中のプロジェクトrepete_swiftの名前をrepeteに変更したい

Renaming a Project or App
こちらのAppleのドキュメントにしたがってやっていこう

Xcode 7.0.1 + iOS用 + Swiftプロジェクトで実行。

• PROJECTを選択。
ss 2015-10-19 22.46.37

• Utilities(右側ペイン)のIdentity and TypeのNameフィールドで、新たな名前を入力して、Enterキーを押す。
ss 2015-10-19 22.24.56

• ダイアログが出るので、OKを押す。
ss 2015-10-19 22.20.51

これでプロジェクト名が変更される。

やってみたら、変更できた。
ビルドも成功。

以前、プロジェクト名変更しようとして、うまくビルドできなかった記憶があったので、何かトラブルが発生するかと思ったらするっといけた。
こちらの記事(Xcode6 プロジェクト名変更方法 – Qiita)では、Xcode 6での例だけど名前入力だけではうまくいかないとある。

Xcode 7では関連している箇所が漏らさず変更されるようになったのだろうか。

[Xcode]Playgroundに画像ファイルを加える

Playgroundにリソースファイルを加える
Xcode 7.0.1で実行。

Playgroundを作成すると、Editor部分が全面に表示されている。
ss 2015-10-18 6.58.05

右上のボタン(Hide or show the Navigator)を押して、Navigatorを表示する
ss 2015-10-17 23.25.00

左側にNavigatorペインが表示される。
そこに画像ファイルをドラッグすると、Resourcesに画像ファイルが追加される
ss 2015-10-17 23.27.10-2

これで、その画像ファイルをあつかうことができる。


pic01.pngという名前の画像ファイルを加えたときにUIImage?を作成した例。

let image = UIImage(named: "pic01.png")

ss 2015-10-17 23.28.32

右端の丸印(Show Result)で、画像を見ることができる
ss 2015-10-17 23.28.46

アプリをSwift 2.0 + Xcode 7に対応させた

Rendow(走ると再生速度が変わるオーディオプレイヤー)をSwift 2.0 + Xcode 7に対応させました。

もともとSwift 1.2 + Xcode 6で作成したアプリでしたが、Swift 2.0 + Xcode 7に移行しました。
コンバートで、varからletへの書き換えや、errorを使っているところをtry~catchに書き換えられましたが、そのあと、実行してみると大量に警告、注意がでました。

以下、出て来た警告、注意とどのように対応したかを記しました。

Initialization of immutable value ‘h’ was never used; consider replacing with assignment to ‘_’ or removing it

変更前

        let h = graphSize.height

変更後

        //let h = graphSize.height

使っていない変数があったのでremoveしました

sectionIndexTitlesForTableView’ has different optionality than expected by protocol ‘UITableViewDataSource’

変更前

   /// 右端indexで表示される頭文字
    func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]! {  ... }

変更後

   /// 右端indexで表示される頭文字
    func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]?  { ... }

型が~!(Implicitly unwrapped optional型)から、~?(Optional型)に変わっていたので変更

そのほか、Swift 1.2では~?(Optional型)になっていた引数が、Swift 2.0ではOptional型でなくなっていたりしたので、そこも修正

Cannot convert value of type ‘[NSObject : AnyObject]’ to expected argument type ‘[String : AnyObject]?’

変更前

            let attr: NSDictionary = [NSFontAttributeName: font, NSForegroundColorAttributeName: color]
            let textSize:CGSize = text.sizeWithAttributes(attr as [NSObject:AnyObject])

変更後

            let attr: [String : AnyObject]? = [NSFontAttributeName: font, NSForegroundColorAttributeName: color]
            let textSize:CGSize = text.sizeWithAttributes(attr)
directory not found for option ‘-F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.sdk/Developer/Library/Frameworks’

~Testsに対してこの「ディレクトリが見つからない」というメッセージが出ていたので、対処
~Tests > Build Settings > Search Paths > Framework Search Paths
そこに
iphoneos/Developer/Library/Frameworks
があるのを削除しました

All interface orientations must be supported unless the app requires full screen.

「(全画面アプリにするのでなければ、)4つの向きに対応しなさい。」
これは、iOS9から登場したiPad用のSlide Over, Split View対応のためでしょう。
Adopting Multitasking Enhancements on iPad: Slide Over and Split View Quick Start
このAppleのドキュメントを読むと、”Supproted interface orientations (iPad)”キーに4つの向きを加えるとあり、iPhoneのほうは、向きを増やさなくても良い模様。
いったんは、すべての向きに対応して、iPad用をSplit Viewに対応しましたが、
iPad/iPhoneは、シェイクすると、テキスト入力のUndoができる | nackpan Blog
の記事に書いたように、揺れるとテキスト入力キャンセルダイアログが出てしまうので、結局Split View対応はやめることにしました。
・General > Deployment Info > Requires full screenにチェックして、全画面アプリとして対処しました。
これで、注意は消えました。

fatal error: swapping a location with itself is not supported

swift2 – fatal error: swapping a location with itself is not supported with Swift 2.0 – Stack Overflow
How do I shuffle an array in Swift? – Stack Overflow
自身とswapすることは、Swift 2.0では禁止とのことで
変更前

            swap(&element[i], &element[j])

変更後

            if i != j {
                swap(&element[i], &element[j])
            }

自身とはswapしないように変更しました


基本的には注意メッセージにしたがって、書き換えていくという形で対処できました。
しかし、大量に警告・注意があったので、修正に相当時間がかかりました。

[iOS]View ControllerをNavigation Controllerに組み込む

ViewControllerをNavigation Controllerに組み込む際に、Xcode使用当初にやっていた手順よりも短い手順でできることに気づいたので記します


Interface Builder上で、ViewControllerをNavigation Controller下におくときに、以前は、このような手順を踏んでいました

以前おこなっていた手順

• 最初の状況
ViewControllerがある状態。このviewControllerをNavigation Controllerに組み込みたい
ss 2015-09-12 13.29.16

  1. 右ペインのObject libraryからNavigation Controllerをドラッグしてくる
    ss 2015-09-12 13.29.25
    ss 2015-09-12 13.29.09

  2. Navigation ControllerにくっついているTableView Controllerを削除
    ss 2015-09-12 13.29.45

  3. Storyboard Entry Pointの矢印をNavigation Controllerに移動
    ss 2015-09-12 16.49.34

  4. Navigation ControllerからCtrl+ドラッグでView Controllerへとsegueで結び、そこで表示されたメニューから、root view controllerを選ぶ
    ss 2015-09-12 13.30.08

以上の手順で行っていました

短い手順

• 最初の状況
ViewControllerがある状態。このviewControllerをNavigation Controllerに組み込みたい
ss 2015-09-12 13.29.16

  1. ViewControllerを選択している状態から、メニューのEditor > Embed In > Navigation Controller
    ss 2015-09-12 13.35.40

これで、ViewControllerは、Navigation Controllerに組み込まれました。
ss 2015-09-12 17.04.07


Embed Inを用いたやりかたのほうがかなり手早いです
しばらく使わないと忘れてしまうので、ここに記しておきます

[iOS]Xcodeでのローカライズがうまくいかないとき

Xcodeでローカライズ

どうやるんだっけ。
こちらに丁寧に記されてある。ありがたや。
Xcode 5でアプリを多言語対応するまでの全スクリーンショット – Dolice Lab
全行程の画像があるので、これに従っておこなった。

ローカライズが反映されない

NSLocalizedStringを使った部分が反映されない。キーに指定した文字列がそのまま表示されてしまう。
困った。
こんな解説があった。
EZ-NET: iPhone アプリで文字列の一部がローカライズされなくなる
セミコロンがいらんところについてしまっていて、上手くいかなかった例。

これではなかった。

Xcodeでローカライズが反映されない時は、キャッシュをクリア | fieldWalking
キャッシュをクリアしましょう。とのこと。
Product –> Clean
それでもだめなら、
Organizer –> Derived DataをDelete

これをやったら、うまくいった。よかった〜。