2015年11月19日木曜日

NSUserDefaultsにenumを保存

変数currentStageは、下のように定義されてるenum(列挙型)の値が入ってる。
これをUserDefaultsに保存しようと思ったのだが、直接保存できないようなので調べたよ。

enum stage:String {
    case forest
    case beach
    case room
    case wheatField

}


まずそのまま保存するのはどうにもこうにも無理らしいので、扱いやすいrawValueを以下のように設定してやる。
ここではStringにしてる(使い方によってはIntでもいい)。
そのrawValueをString値(または数値)として保存するわけだ。

enum stage:String {
    case forest = "forest"
    case beach = "beach"
    case room = "room"
    case wheatField = "wheatField"

}

保存時
    func saveData() {
        let ud = NSUserDefaults.standardUserDefaults()
        ud.setObject(currentStage.rawValue, forKey: "currentStage")
    }


読み込んだら今度はそれをenum型に戻すんだけど、これがだいぶ悩んだ。
enum型にはメソッドがあって、rawValueを引数にしてやればいいのだな。
んで、引数の型をas! Stringとして強制的にStringに変換して。
さらにXcode様の指示どおり、最後にも!を付けたよ。
多分これでうまくいってる。

読み込み時
    func loadData() {
        let ud = NSUserDefaults.standardUserDefaults()
        //Stringで保存されたcurrentStageを列挙型stageの値に戻す
        currentStage = stage(rawValue: ud.objectForKey("currentStage") as! String)!
    }


もう21世紀なんだから、言語内で扱える型は全て直接読み書きできるようにすべきだと思うんだけど、どうだろうか?
いちいちこんなことで頭悩ますなんてアホらしいよね。


なお、UserDefaultsにあらかじめデータが入っていない状態で読み出そうとするとnilが返るため、きちんとチェックしておかないと最悪アプリが落ちることになる。
下記のようにif文のオプショナルバインディングというのを使うとコードが少し楽。
ud.objectForKey("currentStage")の結果がnilじゃなかったら値をtestに代入し、nilだった場合はtestにはfalseが入るためにelse文が実行されるというSwiftの仕組み。

        if let test = ud.objectForKey("currentStage") {
            //Stringで保存されたcurrentStageを列挙型stageの値に戻す
            currentStage = stage(rawValue: test as! String)!
            print("保存済み")
        } else {
            print("保存されてない")
        }

Xcode 7でfailed to code signと出た

Xcode 7で、iPadで実機確認しようとしたら、
「failed to code sign」
というダイアログが出てコンパイルできなかった。

なんか
No valid signing identities (i.e. certificate and private key pair) matching the team ID “XXXXXXXX” were found.

Xcode can attempt to fix this issue.
This will reset your code signing and provisioning settings to recommended values and resolve issues with signing identities and provisioning profiles.

The selected team's agent, '俺の名前' must agree to the latest Program License Agreement.  Please visit the Member Center.
https://developer.apple.com/membercenter
とかも出て、要するに自分のteamIDが認証できないらしい。

Fix itとかボタンがあったんで、その通りに押してもダメ。

最後のメッセージを読むに、プログラムライセンスの改定があったから、ブラウザからデベロッパーセンターにアクセスして、agreeしろということのようだ。

そういえば数日前に「Updates for apps on iTunes Connect.」とかいうメールが来てたっけな。
それと関係あるんだろうか。まあいいや。

早速agreeしたが、すぐにはXcodeには反映されないようで、お仲間がいるかどうかググって5分くらいしてから再度試したらうまくいったよ。(直接のお仲間はいなかったみたい)
その際、たしかデベロッパー登録してあるパスワードを入力致しました。

2015年11月16日月曜日

UIButtonの画像のアニメーション

UIImageViewに表示するUIImageはパラパラアニメができるけど、UIButtonに表示する画像もアニメーションができる。
これで、点滅するボタンとかが作れるわけだ。

UIButtonに画像を表示する機能はUIImageViewを使ってるようで、UIButtonのimageViewプロパティにアクセスする。
このプロパティ、リファレンスを読むとreadOnlyってなってるんだけど、さらにその配下にあるanimationImagesなどのプロパティには値をセットすることができる。

やり方はUIImageViewと一緒で、アニメのコマを入れた配列を用意し、animationImagesに設定。
Duration(時間)やRepeatCount(繰り返し回数。0だと無限繰り返し)を設定し、startAnimation()関数で開始する。

唯一注意点はUIButtonのsetImage()関数で、UIButtonに画像を表示する設定にしてやることだ。
通常の、文字だけ表示する設定じゃダメってことね。

        let imageArray:[UIImage] = [
            UIImage(named: "image01")!,
            UIImage(named: "image02")!
        ]
        animeBtn.setImage(imageArray[0], forState: .Normal) //これ必要
        animeBtn.imageView?.animationImages = imageArray
        animeBtn.imageView?.animationDuration = 1.0
        animeBtn.imageView?.animationRepeatCount = 0

        animeBtn.imageView?.startAnimating()

2015年11月3日火曜日

通知センターに通知を表示する

メールを受信した時や、スケジューラの予定時刻が来た時などにお知らせを出すiOSの通知センターに、アプリから通知を表示する方法。

AppDelegate.swiftにあるapplication()関数に以下のようなコードを書く。

    //AppDelegate.swiftの起動時に呼ばれる関数
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        //現在の通知を削除
        UIApplication.sharedApplication().cancelAllLocalNotifications();
        
        //通知登録前のおまじない
        //これがないとpermissionエラーが発生する
        application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes:
            [UIUserNotificationType.Sound,
            UIUserNotificationType.Alert,
            UIUserNotificationType.Badge],
            categories: nil))
        
        //通知の設定
        let notification:UILocalNotification = UILocalNotification()
        notification.fireDate = NSDate(timeIntervalSinceNow: 10)
        notification.timeZone = NSTimeZone.defaultTimeZone()
        notification.alertBody = "10秒たちました"
        notification.alertAction = "はい"
        notification.soundName = UILocalNotificationDefaultSoundName
        
        //通知登録
        UIApplication.sharedApplication().scheduleLocalNotification(notification)

        return true

    }

通知の設定以下〜return trueの前までは、べつにAppDelegate内に書かなくてもいいので、普通のViewController.swiftなどのしかるべきところに書いてもいい。
「アプリを切り替えてアクティブじゃなくなったら10秒後に通知」とかもできる。
上記のコードでは音を鳴らすようにしているが、soundNameをコメントアウトすれば鳴らない。

通知のダイアログ(iOSの個々の設定次第で出方が変わる)をタップすると、勝手に自分のアプリをアクティブにしてくれる。

通知をタップした時の処理

単にアプリをアクティブにしたのではなく、「通知をタップしてアクティブになった時」に処理をする場合は以下の関数で。
引数notification.alertActionで、上で設定したalertAction(はい)が取り出せる。
(使用不推奨のUIAlertViewを使ってるのはAppDelegate.swift内ではUIAlertControllerだとうまくいかなかったから。ま、気にすんな)

    //LocalNotificationがタップされた時に実行される
    func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
        let alert = UIAlertView();
        alert.title = "受け取りました";
        alert.message = notification.alertBody;
        alert.addButtonWithTitle(notification.alertAction!);
        alert.show();
    }

2015年11月2日月曜日

アプリをバックグランドにした時などに処理を行う

使用中のアプリを別のアプリに切り替えた時、また別のアプリから戻ってきた時に特定の関数を呼び出す方法。

NSNotificationCenterクラスを使うようだ。
引数nameにUIApplication〜と指定することでどういうタイミングでselectorの関数をコールするか選択する。

別のアプリから戻ってきた時(アプリ起動時にも)
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: "forActive",
            name: UIApplicationDidBecomeActiveNotification,
            object: nil)

別のアプリに切り替えた時        
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: "forBackground",
            name: UIApplicationDidEnterBackgroundNotification,
            object: nil)

以下のような値が用意されている。
調べたらもっとあるようだが、だいたい以下のもので事足りるのではないか?
アプリがどうかなる直前、直後などの細かい状態の変化でいろいろ重複して呼ばれるのはViewDidLoadやViewWillAppear関数なんかと一緒ね。

  • アプリがアクティブになった時/別のアプリから戻ってきた時
    • UIApplicationDidBecomeActiveNotification
  • アプリがバックグランドになる時
    • UIApplicationDidEnterBackgroundNotification
  • アプリ起動直後
    • UIApplicationDidFinishLaunchingNotification
  • アプリがアクティブになる直前
    • UIApplicationWillEnterForegroundNotification
  • アプリがアクティブでなくなる直前
    • UIApplicationWillResignActiveNotification
  • アプリ終了直前
    • UIApplicationWillTerminateNotification
  • デバイスの向きが変わる直前
    • UIApplicationWillChangeStatusBarOrientationNotification
  • デバイスの向きが変わった直後
    • UIApplicationDidChangeStatusBarOrientationNotification
参考:
Objective-C:NSNotificationCenterでアプリ起動・終了時にメソッドを呼び出す