2015年9月29日火曜日

UIViewAnimationとかのoptionsの指定が変わった

Objective-CやSwift 1.xでは、UIViewのanimationWithDurationメソッドとかではアニメーションの速度の変化などをoptionsという引数にバイナリーのビット演算で渡してた。

let opt = UIViewAnimationOptions.Autoreverse | UIViewAnimationOptions.CurveEaseIn

UIView.animateWithDuration(duration: 3.0, delay: 0, options: opt, animations: 以下略)

でもSwift 2.0から仕様が変わりやがりまして、[ ]内に , 区切りで並べるようになった。
let option: UIViewAnimationOptions = [UIViewAnimationOptions.Autoreverse, UIViewAnimationOptions.CurveEaseIn]

うっかり古い書き方で書くと、
Binary operator '|' cannot be applied to two 'UIViewAnimationOptions' operands
というエラーが出てしまうので注意だ。
いろいろ変わって面倒くさいね。

2015年9月21日月曜日

AnyObject型に入れたクラスのメソッドが実行できない?

Swiftには、Objective-Cのid型にあたる汎用型のAnyObject型というのがあるわけだ.

しかし、以下のようにClassAにmethodAというメソッドとaというプロパティが設定されていた場合、型推論でClassA型になった変数obj1についてはメソッドもプロパティもいじくれるが、
//うまくいく例
let obj1 = ClassA()
obj1.methodA()
obj1.a = 100


AnyObject型として定義した場合はメソッドもプロパティもいじれず、エラーになってしまう。
//うまくいかない例
let obj2: AnyObject = ClassA()
obj2.methodA()
obj2.a = 100

一つの変数にいくつものクラスを入れて使いまわしたいと思ったのだが、ダメなのかな?

ちなみにObjective-Cでも同じこと試してみたけどダメだった。
ただし、プロパティはSetter/Getterメソッド使えばアクセスできた。

2015年9月18日金曜日

iOS9とXcode7になって

9月17日、iOS9がリリースされたのでさっそくうちのiPad3にインストールした。
多少挙動が怪しいアプリもあるが、今のところ特に大きな問題は出ていない。
動作速度もiOS8.4に比べて遅くなったとは思えない。
目玉機能のSplit ViewやSlide Overといった機能は最近のiPad、iPhoneでしか使えないんだね。うちのでは使えなくて残念。

ま、そりゃいいのだが、自作アプリで動作がおかしい物を発見。

多言語の判断が変わった

多言語対応しているSkyReporterで、コード内で言語別にメッセージを変えている部分が誤動作して、英語のメッセージが出るようになってしまった。
これは言語環境を調べる関数でAPIが"ja"じゃなくて"ja-JP"とかを返すようになったためだという。
こんなとこいちいち変えるなよ! と思う。
iOS8以前にも対応するには文字列の先頭が"ja"かどうかで判断した方がいいようだ。
後でアップデートせねば。


Swiftの仕様が少し変わった模様

自作のSwiftプロジェクトをXcode7で開いたところ、「コードの書き方が変更になったからコンバートしちゃっていい?」ってメッセージが出た。
なんか、Swift 2.0にバージョンアップされたんだとか。
どこがどうコンバートされるのかは確認できる。
たとえばObjective-CのNSLogにあたるprintln()がprint()関数になった模様。

他にもコンバートできなかった箇所がエラーになったり。optionalの関係で、配列にしまうUIImageに!を付けてやったら通った。
optionalは今でもまだよくわかってないのに、また仕様が変わったら困るな。
そうでなくても手持ちの参考書と現在のSwiftの仕様が違ってきている。


ディレクトリが見つからないと出る

Xcodeで開いたら以下のWarningが出て、消えない。

Directory not found for option '-F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator9.0.sdk/Developer/Library/Frameworks'

iPhoneシミュレータのiOS9.0のSDKのディレクトリが見つかんねーぞ、こら、ってことなんだろうけど、俺何もしてないぞ。
プロジェクトの中のBuild Settings、「アプリ名Tests」の中のSerch PathsのFramework Serch Pathsの中を消せばいいようだ。
左側のコラムにPROJECTとTARGETSがあって、TARGETSの中にさらにアプリ名とアプリ名Testsというのがあり、それぞれにSearch PathsのFramework Search Pathsという設定があるので間違えないように。
文字で書くより以下の画像を見たほうがわかりやすかろう。

2015年9月4日金曜日

AlertControllerの書き方


SwiftでAlertを表示しようと思い、AlertControllerのコードを書いたのだが、表示されない。

ログには

whose view is not in the window hierarchy!

などというエラーメッセージが表示されている。

調べたら、viewDidLoad()中に書いていたせいで、viewDidAppear()中に書きなおしたら表示できた
viewDidLoad()の段階ではviewの情報が読み込まれただけだから、画面上の部品の階層がはっきりせず、「そんな段階でさらにAlertなんて表示できるかい!」と言われたわけなのだな。
Objective-Cでやってた頃はうまくいったような気もするのだが、Swiftや最近のiOSで仕様が変わったのかもしれん。まあいいや、覚えておけば。


具体的な書き方は以下。

    override func viewDidAppear(animated: Bool) {
        self.showAlert(title: "タイトル", message: "・メッセージ", actionTitle: "確認")
    }
    
    //AlertControllerで表示
    func showAlert(#title: String, message: String, actionTitle: String)
    {
        let alertCtl = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert) //UIAlertControllerStyle部分は省略可
//alertCtlにボタンごとの情報を追加する
        alertCtl.addAction(
            UIAlertAction(
                title: actionTitle,
                style: UIAlertActionStyle.Default, //デフォルトボタン
                handler: nil//ボタン押下時の処理はhandler:にクロージャとして書く
        )
        
        alertCtl.addAction(
            UIAlertAction(
                title: "キャンセル",
                style: UIAlertActionStyle.Cancel, //Cancelボタンは一番下に出る
                handler: nil)
        )
        
        alertCtl.addAction(
            UIAlertAction(
                title: "削除",
                style: UIAlertActionStyle.Destructive, //Destructiveは赤字になる
                handler: nil)
        )
        
//実際のAlert表示処理
        presentViewController(alertCtl, animated: true, completion: { println("アラート表示しました")}) //頭にself.を付けても付けなくてもどっちでもいい
//表示後の処理はcompletion:にクロージャとして書く
    }



なおiPadで表示する場合、preferredStyle: が.Alertならいいが、.ActionSheetの場合は以下のpopoverPresentationContollerのプロパティを設定してやらないとアプリが落ちる。
ActionSheetはiPhoneなら下からニョキって出るだけだけど、iPadの場合は画面上のどこかから吹き出しの形で出るみたいね。(ほとんど使ったことない)
だからどこから吹き出すのかなんかを指定してやらんといかんようだ。
なお、iPhoneで表示する場合は無視されるので、事前にデバイスを判断するif文などは必要ない。

        //For ipad And Univarsal Device
        alertCtl.popoverPresentationController?.sourceView = self.view

        alertCtl.popoverPresentationController?.sourceRect = CGRect(x: (self.view.frame.width/2), y: self.view.frame.height, width: 0, height: 0)

2015年9月2日水曜日

ImageViewの画像がはみ出す

ImageViewに画像を表示させる場合、画面の縦横比が合わないと見にくいことになるので、ViewのModeで、Aspect FitとかAspect Fillとかを設定することが多いだろう。
Aspect FillにすればImageViewの縦横比と実際の画像の縦横比が違っても、画像の縦横比を崩さずに拡大して表示してくれる。

下の例だと、「会話」などのボタン表示スペースを空けて、その上までがImageView担っている。
(TextViewのテキストが途中でブチ切れてるのはXcodeの仕様なんで、イラっとしても今は気にしない)

期待する表示

ところがビルドしてみると画像がはみ出してボタンにかかってしまい、見づらくなってしまった。
Constraintsを調べても間違っているようには思えない。
期待に応えてくれなかった表示

調べたところ、ImageViewのインスペクタから、DrawingのClip Subviewsボタンにチェックを入れれば直った。

DrawingのClip SubviewsをON


デフォルトでは親View(この場合ImageView)に載ってる子View(この場合画像自体だろう)が親よりでかい場合、はみ出すよう(Clipしない)になっているのだな。
それでチェックをすれば親の大きさの範囲でClipしてくれるようになるわけだ。
めでたしめでたし。

Viewの上にButtonを貼ってあるとか親子関係がわかりやすいならともかく、ImageViewと画像まで親子関係を求められるとは思わんかった。
普通はImageViewの範囲でClipされるのをデフォルトにすべきだと思うんだが…。他の部品と合わせちゃったのかね。
期待どおり