電子の海をたゆたう

iOS初心者がXcodeでアプリ開発を学んでいく日記です。

UIAlertControllerをSwiftで使ってみた

iOS8からUIAlertViewが非推奨なっているとのことで、
UIAlertControllerをSwiftで書いてみました。
新しい手法はどんどん取り入れていって取り残されないようにしていかないとですね。

では、UIAlertControllerとはどういうものなのかというところから
iOS8でSwiftにおけるUIAlertViewが廃止されるため、
UIAlertControllerに切り替える必要があると。
公式から今日からそれ公式のやり方じゃないから、新しいやり方でよろしくねという無茶振り。

UIAlertControllerの特徴

・ボタンの作成はUIAlertActionというクラスを使ってボタンを追加する
・アラートの種類としてはAlertとActionSheetの2種類がある
・ボタンの表示スタイルは3種類

実際のコード

*UIAlertControllerでAlert表示

func showAlert(){
        
        let alertCtr = UIAlertController(title: "Alert",
                                         message: "show alert",
                                         preferredStyle: .Alert)
        
        let cancelAction:UIAlertAction = UIAlertAction(title: "Cancel",
                                                       style: UIAlertActionStyle.Cancel,
                                                       handler:{
                                                        (action:UIAlertAction!) -> Void in
                                                        print("Cancel")
        })
        
        let defaultAction:UIAlertAction = UIAlertAction(title: "Default",
                                                        style: UIAlertActionStyle.Default,
                                                        handler:{
                                                            (action:UIAlertAction!) -> Void in
                                                            print("Default")
        })
        
        let destructiveAction:UIAlertAction = UIAlertAction(title: "Destructive",
                                                            style: UIAlertActionStyle.Destructive,
                                                            handler:{
                                                                (action:UIAlertAction!) -> Void in
                                                                print("Destructive")
        })
        
        alertCtr.addAction(cancelAction)
        alertCtr.addAction(defaultAction)
        alertCtr.addAction(destructiveAction)
        
        presentViewController(alertCtr, animated: true, completion: nil)
    }

*UIAlertControllerでActionShett表示

func showActionSheet(){
        
        let alertCtr = UIAlertController(title: "ActionSheet",
                                         message: "show ActionSheet",
                                         preferredStyle: .ActionSheet)
        
        let cancelAction:UIAlertAction = UIAlertAction(title: "Cancel",
                                                       style: UIAlertActionStyle.Cancel,
                                                       handler:{
                                                        (action:UIAlertAction!) -> Void in
                                                        print("Cancel")
        })
        
        let defaultAction:UIAlertAction = UIAlertAction(title: "Default",
                                                        style: UIAlertActionStyle.Default,
                                                        handler:{
                                                            (action:UIAlertAction!) -> Void in
                                                            print("Default")
        })
        
        let destructiveAction:UIAlertAction = UIAlertAction(title: "Destructive",
                                                            style: UIAlertActionStyle.Destructive,
                                                            handler:{
                                                                (action:UIAlertAction!) -> Void in
                                                                print("Destructive")
        })
        
        alertCtr.addAction(cancelAction)
        alertCtr.addAction(defaultAction)
        alertCtr.addAction(destructiveAction)
        
        presentViewController(alertCtr, animated: true, completion: nil)
    }

UIAlertController宣言時に、preferredStyleをAlertにすることで以下の画像の様にすることができます。

ActionSheetにすることで以下のようにすることが可能です。

ボタンの数はUIAlertActionで増やすことができ、デフォルトで用意されているのが
・UIAlertActionStyle.Cancel (キャンセルボタン)
・UIAlertActionStyle.Default (デフォルトボタン)
・UIAlertActionStyle.Destructive (赤文字ボタン)

の3種類。

個人的に便利だなと感じたのが、preferredStyleのAlertでボタンが2つの時とそれ以上の時は自動でボタンが3つ縦に並ぶのはすごくスマートだと感じました。

簡単な使い方としてはこんな感じです。今回GitHubに公開してみました。
しょぼいコードですが、オープンなところにコードを晒すことで自分のプラスになることが増えるといいなと思います。
github.com

GitHubに登録してみる

先月の更新をだいぶ先延ばしにしてしまった...。
軽いものですが何個か投稿。

GitHubを使って、自分のコード少しづつ貯蓄できたら今後に役立ちそうなので、
アカウント登録の備忘録を残しておこうと思います。
といても、すごく簡単だったので今回は短いです。


GitHubサイトにアクセス
github.com


必要項目に入力する
ユーザー名(ニックネームにしました)、メアド、パスワードを入力して Sign up for GitHubボタンをクリック

プラン選択
無料で使いたいので Unlimited public repositoryes for free.を選択。
こちらのプランは無料で使えるけどpublicに公開されるよというもの。privateで使いたい場合は下の、
Unlimited private repositories for $7/month. (view in JPY)を選択する。(月7ドルの模様)
そのままFinish sign upを押して次の画面へ

Sing upが完了
登録したメールアドレスにメールが送られているので、そちらを確認

メール確認してGitHubに接続する
Please verify your mail address.という件名でメールが送られてくるので、Verify email addressボタンをクリックして、
GitHubに接続して完了。

思ったより簡単でした。

macアプリにチャレンジしてみた

swiftの勉強進めてます。

if文に括弧つけなくてよかったり、セミコロンを最後につけなくてよかったり

swiftに慣れすぎると別の言語を書く際に文法がごちゃごちゃになって大変なことになりそうです。

でも最近は簡略化してて逆に見やすいと感じるようにもなってきました。とりあえず勉強進めて実用レベルまで持って行きたいところです。

今回の投稿はタイマーを作る機会があったのでiPhoneアプリではなく、

macアプリを自分なりに作ってみたのでそれに関しての備忘録です。

macアプリ、開発者登録してないと起動時に怒られますね・・・。

でもリリースするわけではないのでとりあえずスルーしました。

配布もごく一部に限定的に行うのでシステム環境設定のセキュリティとプライバシーからダウンロードしたアプリケーションの実行許可をすべてのアプリケーションを許可にしてもらったりと、使ってもらう側にも設定してもらう必要ありでした。

では、簡単に本題に今回作ったのはニキシー管風味のタイマーアプリです。

完成したものはこれ

ビジュアルは割とよくできたかなと思ってます。

では簡単に手順を説明します。なにぶんmacアプリを作ったのが初めてなのでいろいろ間違ったところもあると思いますが、ご容赦ください。

というか、そもそもmacアプリに関する資料が少ない・・・検索をかけてもiPhoneアプリの記事ばかりでやりたいことと微妙に食い違いがあってアプリ作成には難航しました。

はい、話進めます。

まず最初にやったのはXcodeの新規Project作成でOS XCocoa Application>言語をSwiftにしてプロジェクトを作成。

前回で折角ストーリーボードを学んだのでどうせならと思い、UI周りは全てストーリーボード上で作成してみました。

AutoLayoutだいぶ使いこなせるようになったからmacアプリでも楽勝だろうと思ってた時期がわたしにもありました。Main.storyboarを開いてみたらSceneが二つある・・・。

どういうことなのと思いましたがどうやらUIを設置できるのは下にある方のView Controller Sceneの模様。上に設置されているWindow Contoller Sceneはどうやらヘッダー的なやつらしい。いわゆる「ファイル、編集、表示、履歴、ヘルプ」などを設定する箇所みたいです。起動とクローズができればよかったので今回は特にこちらは触らずにすすめました。

先に画像を使わない単純なタイマーを作ろうと思い、StartボタンとResetボタンを設置し、制限時間のTextFieldを設置しました。

これにTextFieldとButton類をViewContoller.swiftにアウトレット接続して、
ボタン類のみActionを作成します。


とりあえずタイマーとしては完成したのですが、上司からフィードバックをいただきどうせやるならもう少し凝りたいなと思い、
Photoshop ニキシー管風味カウンター素材 - trismegistuslabo

こちらのサイトから素材をお借りして、ニキシー管風タイマーを作ってみることに。

すごく原始的ですが、タイマーの文字列を1文字づつみて対応した数字の画像で更新し続けるというプログラムを書いてみました。

作成したコードがこちら
ViewContoller.swift

import Cocoa
import AVFoundation

class ViewController: NSViewController{
    
    var utils = Utils()
    var audioPlayer:AVAudioPlayer!
    
    var counter = 0
    var normalTimer = NSTimer()
    var overTimer = NSTimer()
    
    var limitTime = "05:00"//05:00
    var overTimerCount = "00:00"
    
    var timer_flg = false
    var start_button_tapped = false
    var over_timer_flg = false
    var watch_flg = false
    
    @IBOutlet var semicolon: NSImageView!
    @IBOutlet var first_minute: NSImageView!
    @IBOutlet var second_minute: NSImageView!
    @IBOutlet var first_second: NSImageView!
    @IBOutlet var second_second: NSImageView!
    @IBOutlet var windowOverView: NSImageView!
    
    //ウォッチボタン
    @IBOutlet var watchButton: NSButton!    
    @IBAction func WatchButtonTapped(sender: AnyObject) {
        if watch_flg == false{
            limitTime = "05:00"
            overTimerCount = "00:00"
            startButton2.image = NSImage(named:"nixieStartButton")
            
            normalTimer.invalidate()
            overTimer.invalidate()
            
            timer_flg = false
            start_button_tapped = false
            over_timer_flg = false
            watch_flg = true
            
            startButton2.enabled = false
            resetButton2.enabled = false
            
            normalTimer = NSTimer.scheduledTimerWithTimeInterval(
                1.0,
                target: self,
                selector: #selector(ViewController.nowTime),
                userInfo: nil,
                repeats: true)
            
        }else{
            watch_flg = false
            startButton2.enabled = true
            resetButton2.enabled = true
            
            //強制リセット
            limitTime = "05:00"//05:00
            overTimerCount = "00:00"
            startButton2.image = NSImage(named:"nixieStartButton")
            
            normalTimer.invalidate()
            overTimer.invalidate()
            
            timer_flg = false
            start_button_tapped = false
            over_timer_flg = false
            
            time.stringValue = limitTime;
            self.changeNixieImage()
        }
        
    }
    //スタートボタン
    @IBOutlet var startButton2: NSButton!
    @IBAction func StartButtonTapped2(sender: AnyObject) {
        //スタート処理
        if timer_flg == false && start_button_tapped == false && over_timer_flg == false{
            normalTimer = NSTimer.scheduledTimerWithTimeInterval(
                1.0,
                target: self,
                selector: #selector(ViewController.update),
                userInfo: nil,
                repeats: true)
            
            startButton2.image = NSImage(named:"nixieStopButton")
            start_button_tapped = true
            
        }else{//ストップ処理
            if over_timer_flg == true {//タイムオーバーストップ処理
                
                print(timer_flg)
                print(start_button_tapped)
                print(over_timer_flg)
                
                overTimer.invalidate()
                over_timer_flg = false
                startButton2.image = NSImage(named:"nixieStartButton")
                
                if audioPlayer.play() == true{
                    audioPlayer.stop()
                }
                
            }else{//制限時間ストップ処理
                if over_timer_flg == false && timer_flg == false {
                    normalTimer.invalidate()
                    start_button_tapped = false
                    startButton2.image = NSImage(named:"nixieStartButton")
                }
            }
        }
    }

    //リセットボタン
    @IBOutlet var resetButton2: NSButton!
    @IBAction func ResetButtonTapped2(sender: AnyObject) {
        //強制リセット
        limitTime = "05:00"//05:00
        overTimerCount = "00:00"
        startButton2.image = NSImage(named:"nixieStartButton")
        
        normalTimer.invalidate()
        overTimer.invalidate()
        
        timer_flg = false
        start_button_tapped = false
        over_timer_flg = false
        
        time.stringValue = limitTime;
        
        windowOverView.wantsLayer = true
        windowOverView.layer?.backgroundColor = NSColor(calibratedRed: 0.0, green: 0.0, blue: 0.0, alpha: 0.0).CGColor
        
        if audioPlayer.play() == true{
            audioPlayer.stop()
        }
        self.changeNixieImage()
    }
    
    //タイマー更新
    func update(){
        //制限時間描画処理
        let df:NSDateFormatter = NSDateFormatter();
        df.dateFormat = "mm:ss"
        let dt:NSDate = df.dateFromString(limitTime)!
        let dt02 = NSDate(timeInterval: -1.0, sinceDate: dt)
        limitTime = df.stringFromDate(dt02)
        let strdt02 = df.stringFromDate(dt02)
        time.stringValue = strdt02;
        print(strdt02)
        
        //文字画像切り替えメソッド呼び出し
        self.changeNixieImage()
        
        //制限時間判定
        if self.time.stringValue == "00:00"{
            // タイマーの停止
            normalTimer.invalidate()
            print("end count")
            startButton2.image = NSImage(named:"nixieStopButton")
            
            timer_flg = true
            start_button_tapped = false
            over_timer_flg = true
            
            //画面赤かぶせ
            windowOverView.wantsLayer = true
            windowOverView.layer?.backgroundColor = NSColor(calibratedRed: 1.0, green: 0.0, blue: 0.0, alpha: 0.15).CGColor
            
            //タイムオーバータイマー開始
            overTimer = NSTimer.scheduledTimerWithTimeInterval(
                1.0,
                target: self,
                selector: #selector(ViewController.overTimeUpdate),
                userInfo: nil,
                repeats: true)
            
            //BGM開始
            self.startBGM()
        }
    }
    
    //タイムオーバー
    func overTimeUpdate(){
        let df:NSDateFormatter = NSDateFormatter();
        df.dateFormat = "mm:ss"
        let dt:NSDate = df.dateFromString(overTimerCount)!
        let dt02 = NSDate(timeInterval: 1.0, sinceDate: dt)
        overTimerCount = df.stringFromDate(dt02)
        let strdt02 = df.stringFromDate(dt02)
        time.stringValue = strdt02;
        print(strdt02)
        
        //文字画像切り替えメソッド呼び出し
        self.changeNixieImage()
    }
    
    func startBGM(){
        audioPlayer.numberOfLoops = -1
        audioPlayer.play()
    }
    
    //タイマー画像変更
    func changeNixieImage(){
        let time_str:String = time.stringValue
        let index0 = time_str.startIndex.advancedBy(0)
        let index1 = time_str.startIndex.advancedBy(1)
        let index3 = time_str.startIndex.advancedBy(3)
        let index4 = time_str.startIndex.advancedBy(4)
        
        let first_char:Character = time_str[index0]
        let second_char:Character = time_str[index1]
        let fourth_char:Character = time_str[index3]
        let fifth_char:Character = time_str[index4]
                
        let first_image:NSImage = utils.checkNixieImage(first_char)
        first_minute.image = first_image
        let second_image:NSImage = utils.checkNixieImage(second_char)
        second_minute.image = second_image
        let fourth_image:NSImage = utils.checkNixieImage(fourth_char)
        first_second.image = fourth_image
        let fifth_image:NSImage = utils.checkNixieImage(fifth_char)
        second_second.image = fifth_image
        
        print(first_char)
        print(second_char)
        print(fourth_char)
        print(fifth_char)
        print(time_str)
        
    }
    
    //時計モード
    func nowTime(){
        let date = NSDate() // Dec 27, 2015, 7:36 PM
        let cal = NSCalendar.currentCalendar()
        let comp = cal.components(
            [NSCalendarUnit.Year, NSCalendarUnit.Month, NSCalendarUnit.Day,
                NSCalendarUnit.Hour, NSCalendarUnit.Minute, NSCalendarUnit.Second],
            fromDate: date)
        
        print(comp.hour)
        print(comp.minute)
        
        let hour:String = utils.addZero(String(comp.hour), timeNuber: comp.hour)
        let minute:String = utils.addZero(String(comp.minute), timeNuber: comp.minute)
        
        let hour_index0 = hour.startIndex.advancedBy(0)
        let hour_index1 = hour.startIndex.advancedBy(1)
        let minute_index0 = minute.startIndex.advancedBy(0)
        let minute_index1 = minute.startIndex.advancedBy(1)
        
        let first_char:Character = hour[hour_index0]
        let second_char:Character = hour[hour_index1]
        let fourth_char:Character = minute[minute_index0]
        let fifth_char:Character = minute[minute_index1]
        
        let first_image:NSImage = utils.checkNixieImage(first_char)
        first_minute.image = first_image
        let second_image:NSImage = utils.checkNixieImage(second_char)
        second_minute.image = second_image
        let fourth_image:NSImage = utils.checkNixieImage(fourth_char)
        first_second.image = fourth_image
        let fifth_image:NSImage = utils.checkNixieImage(fifth_char)
        second_second.image = fifth_image
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.wantsLayer = true
        watchButton.image = NSImage(named:"watch_image3")
        watchButton.bordered = false
        watchButton.sizeToFit()
        
        let sound_data = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("bgm", ofType:"mp3")!)
        print(sound_data)
        audioPlayer = try? AVAudioPlayer(contentsOfURL: sound_data)
        
        startButton2.image = NSImage(named:"nixieStartButton")
        startButton2.bordered = false
        startButton2.sizeToFit()
        
        resetButton2.image = NSImage(named:"nixieResetButton")
        resetButton2.bordered = false
        resetButton2.sizeToFit()
        
        let image1:NSImage = NSImage(named: "nixie_0")!
        print(image1)
    }
    
    override var representedObject: AnyObject? {
        didSet {        }
    }
    
    override func awakeFromNib() {
        if self.view.layer != nil {
            let color : CGColorRef = CGColorCreateGenericRGB(0, 0, 0, 1.0)
            self.view.layer?.backgroundColor = color
        }
        
    }
    
}


Utils.swift

import Cocoa

class Utils: NSObject {
    func checkNixieImage(number_str:Character) -> NSImage{
        //var path_file_name = "";
        
        let image0:NSImage = NSImage(named: "nixie_0")!
        
        if number_str == "0"{
            print(image0)
            return image0
            
        }else if number_str == "1"{
            let image1:NSImage = NSImage(named: "nixie_1")!
            print(image1)
            return image1
            
        }else if number_str == "2"{
            let image2:NSImage = NSImage(named: "nixie_2")!
            print(image2)
            return image2
            
        }else if number_str == "3"{
            let image3:NSImage = NSImage(named: "nixie_3")!
            print(image3)
            return image3
            
        }else if number_str == "4"{
            let image4:NSImage = NSImage(named: "nixie_4")!
            print(image4)
            return image4
            
        }else if number_str == "5"{
            let image5:NSImage = NSImage(named: "nixie_5")!
            print(image5)
            return image5
            
        }else if number_str == "6"{
            let image6:NSImage = NSImage(named: "nixie_6")!
            print(image6)
            return image6
            
        }else if number_str == "7"{
            let image7:NSImage = NSImage(named: "nixie_7")!
            print(image7)
            return image7
            
        }else if number_str == "8"{
            let image8:NSImage = NSImage(named: "nixie_8")!
            print(image8)
            return image8
            
        }else if number_str == "9"{
            let image9:NSImage = NSImage(named: "nixie_9")!
            print(image9)
            return image9
        }

        return image0
    }
    
    func addZero(timeString:String,timeNuber:Int)->String{
        if timeString.characters.count==1 {
            return "0\(timeNuber)"
        }else{
            return "\(timeNuber)"
        }
    }
    
}

欲がでしまい、中央のボタンを押すと時計にもなるようになっています。

今回、一番悩んだのがmacアプリでのリソースの配置です。普段わたしはiPhoneアプリで画像などのリソースを参照するとき、パスを指定して参照するのですが、
macアプリでパス指定のリソース参照がうまくできず、うんうん悩んでいた結果、xcassetsを使うといいことに気づきました。

まず、xcassetsファイルに参照したい画像や音データを入れておきます。

コードでは、上記の一部抜粋ですが、

        let sound_data = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("bgm", ofType:"mp3")!)
        print(sound_data)
        audioPlayer = try? AVAudioPlayer(contentsOfURL: sound_data)

こちらのコードでリソースデータを取得することができました。

結果的にできたのが一番上の画像のアプリなのですが、そこそこいいできなのではと思っています。

改善点もちらほらあったので機会があればもっと良くしようかなと思います。

AutoLayoutを使ってImageViewを常に中心に表示する

AutoLayoutをやろうやろうと思っていてずっと先延ばしにしてましたが、触れる機会があったので備忘録として記憶しておこうと思います。
一度やりかたを覚えてしまうと割と簡単だなと思いました。

今回はMain.storyboardを使ってImageViewをどんなサイズのスクリーンでも比率を見て拡大縮小してくれるAutoLayoutを作ってみようと思います。

①. まずは新規プロジェクトを作成し、Main.storyboardを選択します。
  右側の検索フォームでImageと入力し、ImageViewが検索結果に出ると思うので
  storybordの中心に設置しましょう。

    f:id:electronicstar:20160407182320p:plain


②. ImageViewに適当な画像を設定しましょう
  ViewのModeはAspect Fitを選択します。
  ※画像はAssets.xcassetsに設定することをお勧めしますstoryboardに設定する画像は
     なぜかフォルダから直接参照するとビルドした際に実機に画像が反映されていなパターンがありました

③. imageViewが常に中心になるようにしましょう
  画像を常にスクリーンの中央に設置するようにします。
  設置したImageを選択し、AutoLayoutのHorizontally in Containerと Vertically in Containerに
  チェックを入れましょう。
  これでImageが常に中心に描画されます。

    f:id:electronicstar:20160408162518p:plain


④. 設置した画像の横幅とstoryboardの横幅を同じになるようにしましょう
  左側のViewControllerのViewと設置したImageを選択し、AutoLayoutを開きます。
  Equal Widthにチェックを入れてAdd 1 Constraintしましょう。
  これでとりあえず設置した画像が対象スクリーンの横幅に合わせて拡大縮小するようになります。

    f:id:electronicstar:20160408162530p:plain


⑤. 設置した画像を描画スクリーンに対して縮小する
  設置した画像が常に描画スクリーンの横幅80%で表示されるようにしましょう。

    f:id:electronicstar:20160408165213p:plain


以上でiPhoneのどの端末でも横幅に対して80%で中央に画像が表示されるようになったかと思います。
AutoLayoutは使いこなせると色々便利そうなのでこれからも色々勉強してみるつもりです。

NSUserDefaultsがobserveValueForKeyPathで2回呼ばれてる問題

iOS9.3がリリースされ、アプリのテストをした際に発見したバグ?について投稿しておこうと思います。

KVOでNSUserDefaultsのキー値の変化を監視するシステムを作ったのですが、NSUserDefaultが2回、observeValueForKeyPathに呼び出されているようです。

試しにNSUserDefaultsではなく、NSMutableDictionaryで値の監視を行ったところ2回呼び出されず、1回だけログに出力がされました。

なお、この現象はこちらで確認した限りではiOS9.3でのみ起こっており、9.2以下では発生しませんでした。


NSUserDefaultsのコードと出力結果

#import "ViewController.h"

@interface ViewController (){
}
@end

@implementation ViewController
- (void)dealloc{
    [super dealloc];
    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
    [ud removeObserver:self forKeyPath:TESTIOS93_FIRST];
    [ud removeObserver:self forKeyPath:TESTIOS93_SECOND];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
    dict = [NSMutableDictionary dictionaryWithDictionary:@{TESTIOS93_FIRST:@"This is key1.", TESTIOS93_SECOND:@"This is key2."}];
    [ud addObserver:self forKeyPath:TESTIOS93_FIRST options:0 context:nil];
    [ud addObserver:self forKeyPath:TESTIOS93_SECOND options:0 context:nil];
    [self changeKeyValue];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
    NSLog(@"-------------------------");
    NSLog(@"call observeValueForKeyPath");
    if([keyPath isEqualToString:TESTIOS93_FIRST] == YES){
        NSLog(@"TESTIOS93_FIRST CALLING = %@",[ud objectForKey:TESTIOS93_FIRST]);
        
    }else if([keyPath isEqualToString:TESTIOS93_SECOND] == YES){
        NSLog(@"TESTIOS93_SECOND CALLING = %@",[ud objectForKey:TESTIOS93_SECOND]);
    }
}
- (void)changeKeyValue{
    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
    [ud setValue:@"change key1 KVO Test" forKey:TESTIOS93_FIRST];
    [ud setValue:@"change key2 KVO Test" forKey:TESTIOS93_SECOND];
    [ud synchronize];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

出力結果

2016-03-25 12:07:15.442 iOS9.3_KVOTest[1711:469489] -------------------------
2016-03-25 12:07:15.442 iOS9.3_KVOTest[1711:469489] call observeValueForKeyPath
2016-03-25 12:07:15.442 iOS9.3_KVOTest[1711:469489] TESTIOS93_FIRST CALLING = change key1 KVO Test
2016-03-25 12:07:15.442 iOS9.3_KVOTest[1711:469489] -------------------------
2016-03-25 12:07:15.442 iOS9.3_KVOTest[1711:469489] call observeValueForKeyPath
2016-03-25 12:07:15.442 iOS9.3_KVOTest[1711:469489] TESTIOS93_FIRST CALLING = change key1 KVO Test
2016-03-25 12:07:15.442 iOS9.3_KVOTest[1711:469489] -------------------------
2016-03-25 12:07:15.442 iOS9.3_KVOTest[1711:469489] call observeValueForKeyPath
2016-03-25 12:07:15.443 iOS9.3_KVOTest[1711:469489] TESTIOS93_SECOND CALLING = change key2 KVO Test
2016-03-25 12:07:15.443 iOS9.3_KVOTest[1711:469489] -------------------------
2016-03-25 12:07:15.443 iOS9.3_KVOTest[1711:469489] call observeValueForKeyPath
2016-03-25 12:07:15.443 iOS9.3_KVOTest[1711:469489] TESTIOS93_SECOND CALLING = change key2 KVO Test

******************************************************************************************

NSMutableDictionaryのコードと出力結果

#import "ViewController.h"

@interface ViewController (){
    NSMutableDictionary *dict;
}
@end

@implementation ViewController
- (void)dealloc{
    [super dealloc];
    [dict removeObserver:self forKeyPath:TESTIOS93_FIRST];
    [dict removeObserver:self forKeyPath:TESTIOS93_SECOND];
}
- (void)viewDidLoad {
    [super viewDidLoad];
    dict = [NSMutableDictionary dictionaryWithDictionary:@{TESTIOS93_FIRST:@"This is key1.", TESTIOS93_SECOND:@"This is key2."}];
    [dict addObserver:self forKeyPath:TESTIOS93_FIRST options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
    [dict addObserver:self forKeyPath:TESTIOS93_SECOND options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
    [self changeKeyValue];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    NSLog(@"-------------------------");
    NSLog(@"call observeValueForKeyPath");
    
    if([keyPath isEqualToString:TESTIOS93_FIRST] == YES){
        NSLog(@"TESTIOS93_FIRST CALLING = %@",[dict objectForKey:TESTIOS93_FIRST]);
    }else if([keyPath isEqualToString:TESTIOS93_SECOND] == YES){
        NSLog(@"TESTIOS93_SECOND CALLING = %@",[dict objectForKey:TESTIOS93_SECOND]);
    }
}
- (void)changeKeyValue{
    [dict setObject:@"change key1 KVO Test" forKey:TESTIOS93_FIRST];
    [dict setObject:@"change key2 KVO Test" forKey:TESTIOS93_SECOND];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

出力結果

2016-03-25 12:01:26.001 iOS9.3_KVOTest[1676:466919] -------------------------
2016-03-25 12:01:26.002 iOS9.3_KVOTest[1676:466919] call observeValueForKeyPath
2016-03-25 12:01:26.002 iOS9.3_KVOTest[1676:466919] TESTIOS93_FIRST CALLING = change key1 KVO Test
2016-03-25 12:01:26.002 iOS9.3_KVOTest[1676:466919] -------------------------
2016-03-25 12:01:26.002 iOS9.3_KVOTest[1676:466919] call observeValueForKeyPath
2016-03-25 12:01:26.002 iOS9.3_KVOTest[1676:466919] TESTIOS93_SECOND CALLING = change key2 KVO Test

ちらほらと報告が上がってるみたいですが、このようなことは初めて経験したので正直焦りました...

Xcodeがおかしい?アプリがアップできなくなってたエラー -Missing iOS Distribution signing identity for-

一昨日はすごく暖かかったのに、昨日からまた寒くなりました・・・

あったかい方が過ごしやすいのに・・・

 

さて、業務中にアプリをアドホックにあげてテストする機会があったので

いつもの作業手順でアドホックにあげたのですが、

なぜかアップできない状態に。

 

Xcodeからはこんなエラーが「Missing iOS Distribution signing identity for ???」

どうやらAppStoreにもアップできない模様。

他にも起きてる人いるのか調べて見たら

 

 

stackoverflow.com

 

みなさん起きてしまっているようでした。

 

上記の参考サイトと同じ手順で、

1.キーチェーンアクセスを開く

 

2.キーチェーンアクセスの左側の項目のシステムを選ぶ

f:id:electronicstar:20160216174736p:plain

 

3.メニューバーの表示>有効期限の切れた証明書を表示をクリック

f:id:electronicstar:20160216174906p:plain

 

4.AppleWorldwide Developer Relations Certification Authorityの有効期限が切れているので削除する

f:id:electronicstar:20160216174949p:plain

 

5.以下の証明書をDLしてキーチェーンアクセスのシステムに追加

https://developer.apple.com/certificationauthority/AppleWWDRCA.cer

 

Xcodeを再起して、再び試してみたところうまくいかず

証明書を変えたからもしやと思ってDeveloperCenterを見てみたら

Provisioning Profilesが全てInvalidかExpiredに・・・

「うわぁ、なんだか凄いことになっちゃったぞ」

 

とりあえず自分で発行していた証明書も入れ替える必要があるようなので、

引き続き作業

1.Apple Developer CenterにログインしてCertificates, Identifiers & Profilesを選択

2.iOS AppsのCertificatesを選択

3.Development>該当する証明書を選択>Revokeする

f:id:electronicstar:20160216182043p:plain

 

 4.以下のサイトを参考にRevokeしたものと同じ証明書を作り直し

mmorley.hatenablog.com

 

5.証明書を更新したので、Provisioning Profilesを更新する

   Provisitoning Profilesの該当するNameを選択してEditする

f:id:electronicstar:20160216183904p:plain

 

6.Certificatesのチェックボックスが外れているので、チェックを入れてGenerateする

f:id:electronicstar:20160216184123p:plain

 

7.更新完了したのでDownloadする

f:id:electronicstar:20160216184239p:plain

 

8.更新したProvisioning ProfileをダブルクリックしてiPhone構成ユーティリティのプロビジョニングプロファイルに追加されているのを確認する

f:id:electronicstar:20160216184842p:plain

 

9.Xcodeを再起動して更新した署名とProvisioning Profileに変更してビルド

 

上記の流れで私はうまくビルドできました。

長くなりましたが、役に立てれば幸いです。

 

余談:今週の土日にサイクリングに久しぶりに行ってきまして、住んでいる宮前区から羽田空港周辺まで自転車で行って蒲田駅を経由し、多摩川沿いを走ってきました。

天気が良かったからすごく走りやすかった。

f:id:electronicstar:20160216185254j:plain

【iOS】アプリからインスタグラムに投稿する

 最近、急に寒くなって朝が辛くなってきました。

東京の寒さと九州の寒さはやはりちょっと違うと改めて感じたこの頃です。

 

 

今回、作成しているアプリからInstagramに投稿する機能を実装してみました。

 

アプリからハッシュタグを送りたいなと思っていたので、

Instagramハッシュタグを送るコードを試しに入れてみたのですが、

何度試しても、Instagramに反映されない・・・

 

なんでや・・・・と思って調べた結果、

Instagram公式にこのようなことが書かれていました。

http://developers.instagram.com/post/125972775561/removing-pre-filled-captions-from-mobile-sharing

英語があまり得意じゃないので、わかるのに時間がかかったのですが

どうやらFacebookなどと同じようにアプリからハッシュタグを送れな異様になったとか。

 

仕方なしに、ハッシュタグなしで実装した結果、

UIDocumentInteractionControllerを使うといいとのことなので

やってみました。

 

-----------------------------------------------------------------------------------------------------

Instagram.h

Objective-C

@interface SnapshotView : UIView<UIDocumentInteractionControllerDelegate>{}

 

@property (strong) UIImageView *imgView;

@property(nonatomic,retain)UIDocumentInteractionController *interactionController;

-----------------------------------------------------------------------------------------------------

Instagram.mm

Objective-C

- (void)openInstagram{

    NSURL *instagramURL = [NSURL URLWithString:@"instagram://app"];

    if(![[UIApplication sharedApplication] canOpenURL:instagramURL]){

        NSLog(@"Not instle Instagram");

        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Message"

                                                                                   message:@"Not install Instagram"

                                                                                    delegate:nil

                                                                      cancelButtonTitle:@"OK"

                                                                      otherButtonTitles:nil];

        [alertView show];

        return;

    }

    

    NSData *imageData = UIImagePNGRepresentation(_imgView.image);

    NSString *filePath =[NSHomeDirectory() stringByAppendingPathComponent:@"Documents/image.igo"];  

    [imageData writeToFile:filePath atomically:YES];

    NSURL *fileURL = [NSURL fileURLWithPath:filePath];

 

    self.interactionController = [UIDocumentInteractionController interactionControllerWithURL:fileURL];

    self.interactionController.UTI = @"com.instagram.exclusivegram";

    self.interactionController.delegate = self;

 

    BOOL present = [self.interactionController presentOpenInMenuFromRect:self.frame

                                                                                                               inView:self

                                                                                                           animated:YES];

      if(!present){

          UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Message"

                                                                                     message:@"miss"

                                                                                      delegate:nil

                                                                        cancelButtonTitle:@"OK"

                                                                        otherButtonTitles:nil];

          [alertView show];

      }

 } 

 

#pragma mark - UIDocumentInteractionControllerDelegate

 

- (void)documentInteractionController:(UIDocumentInteractionController *)controller

        willBeginSendingToApplication:(NSString *)application

{

    

}

 

- (void)documentInteractionController:(UIDocumentInteractionController *)controller

           didEndSendingToApplication:(NSString *)application

{

    //[self closeView];

}

 

- (void) documentInteractionControllerDidDismissOpenInMenu: (UIDocumentInteractionController *) controller

{

    // キャンセルで閉じたとき

    //[self closeView];

}

-----------------------------------------------------------------------------------------------------

 

これでなんとかInstagramに画像を送ることができました。 

おかしい箇所などがありましたら、コメントをいただけると幸いです。