リブートキャンプ by Swift 目次

 

 予告通り地図画面起動ボタン付けます。

 

 まずはボタンの画面への貼り付けから。

 この場合、ライブラリからボタンを探さないといけないんだけど、前回のTap Gesture Recognizerを探した要領で、検索ボックスに「but」あたりを入れれば一発で出てきます。

 

 この見つけた項目をCanvasビューにドラッグ&ドロップするのはTap Gesture Recognizerの時と同じ。

 

 

 ちょっと違う点は、ボタンの場合、ドロップした位置にボタンが表示されるってこと。ボタンはTap Gesture Recognizerと違って画面の一部になるんで、ビューコントローラが画面を表示した際に、どう見えるのか確認できるようになってるんですよ。

 

 

 画面に貼り付けたら、あとはボタンのタップに合わせた反応を設定するだけっす。

 で、例のターゲットアクションデザインパターンなわけですよ。

 ボタンもTap Gesture Recognizer同様、他オブジェクトとの連携にこのパターン使ってます。ターゲットアクションデザインパターンがわからん人は「タップしてドン」を読みましょう。

 なので、ボタンの反応もTap Gesture Recognizerと同じで、ターゲットとアクションを設定するってことになる。

 異なる点はTap Gesture Recognizerだと、アクションに対する引き金はタップだけだったんだけど、ボタンにはいろいろな引き金があるってとこ。

 

 

 どの引き金に対して、どうアクションするかはプログラマ次第ってわけで、それぞれにターゲットとアクションを指定できます。

 で、今回なら、Touch Up Inside(タッチした指先がボタン上で放された)時に地図を表示が適切なんで、こいつにターゲットとアクションを指定します。

 前回、Tap Gesture RecognizerでやったみたいにTouch Up InsideからView Controllerに向けて接続線を伸ばせば、ドロップ時に現れるメニューからshowAsViewメソッドをアクションに指定することができます。

 

 

 ちゃんと設定できたら、ボタンのConnectionsインスペクタはこんな感じになる↓

 

 

 これで

 

  画面にボタンを置く

  ボタンがタップされたらshowAsViewメソッドを呼び出す

 

という処理がストーリーボードで記述されたことになるわけっすな。

 前回のサンプル:http://tetera.jp/xcc/book-sample/storyboard-2.zipを元に、上の手順に従ってボタンを加え、Runしちゃってみてください。

 画面タップの他にボタンをタップでも地図画面が出るようになります。

 

 

 てなわけで、ボタン用意できたなら画面全体のタップ反応は不要っすね。ボタン以外の画面タップは反応しなくしちゃいましょう。

 

 今現在は、ViewControllerのshowAsViewメソッドが、ボタンおよびTap Gesture Recognizerの両方のターゲットアクションとして登録されてる状態っす。確認したい人はView Controller選んで、Connectionsインスペクタの画面を見ましょう。   

 Referencing Outlut Collectionsグループで、showAsView項目に2つのオブジェクトが接続されてるのがわかると思う。

 


 なので、このうちのTap Gesture Recognizerとの接続を切ればボタン以外の画面タップは反応しなくなります。

 接続を切るのは、接続項目の横にあるxマークをクリックするだけです。

 

 

 ま、これで全然問題ないんですが、そもそもTap Gesture Recognizer自体が不要なわけですよ。なのでTap Gesture Recognizer自体を削除しましょう。

 これはoutlineビューでTap Gesture Recognizer項目を選んで、deleteキーでOK。

 

 Runするとボタン以外反応しなくなったはず。

 

サンプル:

 http://tetera.jp/xcc/book-sample/storyboard-3.zip

 

 ちなみにサンプルではボタンのタイトルを「Button」から「地図」に変えてるけど、これはcancasビュー上のボタンをダブルクリックすれば変更できます。

 

 ボタンを選んだ状態でAttributesインスペクタで変更でも可。どっちでも好きな方でやってください。

 

 

 ViewController.swiftでソースコードとして書くなら、こんな感じっす。ボタンの矩形の値は適当。

    override func viewDidLoad() {
        super.viewDidLoad()
        let button = UIButton(type: .system)
        button.frame = CGRect(x: 150, y: 250, width: 50, height: 44)
        button.addTarget(self, action:#selector(showAsView),
                for: .touchUpInside)
        button.setTitle("地図", for: .normal)
        self.view.addSubview(button)
    }

 

 これがストーリーボード側でInterface Builderを使って記述されたことになるわけですな。

 

 で、いよいよストーリーボードを使うメリットのド本命。Auto Layoutをストーリーボードに記述する方法っす。

ストーリーボードでのAuto Layout記述

 「ダイナミックなレイアウト」で紹介したNSLayoutConstraintの定義をストーリーボードに記述しようって話っす。

 例えば、ボタンを画面の真ん中にしたい時、ソースコードでAuto Layoutを使う場合は、作成したボタン(button)に対して次のような記述が必要になるわけですが…

 こいつをストーリーボードで記述してみます。

                ・・・
        button.setTitle("地図", for: .normal)
        self.view.addSubview(button)
        //  Auto Layout機能追加
        button.translatesAutoresizingMaskIntoConstraints = false
        var constraints = [NSLayoutConstraint]()
        //  横の制約を作成しconstraintsに追加する
        let h_constraint = NSLayoutConstraint(
            item:button, attribute:.centerX,
            relatedBy:.equal,
            toItem:self.view, attribute:.centerX,
            multiplier:1, constant:0)
        constraints.append(h_constraint)
        //  縦の制約を作成しconstraintsに追加する
        let v_constraint = NSLayoutConstraint(
            item:button, attribute:.centerY,
            relatedBy:.equal,
            toItem:self.view, attribute:.centerY,
            multiplier:1, constant:0)
        constraints.append(v_constraint)
        //  制約群を有効にする
        NSLayoutConstraint.activate(constraints)

 

注意)こんな風にダイレクトに記述する方法の他にVisual Format Languageて記述法を使う方法もあります。こっちの方が記述が簡略化できるけど、さらなる文法覚える必要があります。興味がある人はAuto Layout Guideを読んでみましょう。Advanced Auto LayoutのProgrammatically Creating ConstraintsやAppendexで紹介されてます。

 

 てなわけで、さっそく実行。

 まず、canvas画面のボタンが小さすぎて操作しにくい場合は、適度なサイズにcanvasビューをズームしときましょう。

 

 

 でもって、controlキーを押しながらcanvasビュー上のボタンをクリックし、そのままドラッグして線を引っ張り出します。

 

 

 で、この線をドラッグでボタンが置かれてる親ビュー(つまりボタンの枠外)まで伸ばしたところでドロップ。

 これでボタン(ドラッグ元)と親ビュー(ドロップ先)との位置関係制約(NSLayoutConstraint)を作りたいんだってことになり、んじゃ、どういう拘束なんだよとポップアップメニューが出るんでCenter Horizontally in Safe Area項目(親ビュー側の水平線中央とボタンの水平線中央を合わせる)を選びます。

 ちなみにSafe Areaってのは最近導入された領域で、オブジェクトが占める画面領域のうち、ユーザーの操作に支障が出そうにない領域を指します。最近のiOSは画面端からのフリップでいろいろシステム側が動作する(コントロールパネル出したりとか)ので、そういった画面端なんかを含まない領域を指します。

 

 

 とにかくこれで横位置の設定が完了。

 で、これだけだと横位置しか設定されてないから、拘束しきれないよ〜と注意されるので、再びボタン上でcontrolキー+クリックして線引っ張り出して、親ビューの上で放してメニューからCenter Vertically in Safe Area項目を選んでください。

 

 

注意)shiftキー押しながらクリックで、複数の項目の選択を追加したり解除したりできます。この場合、メニューを閉じるときはenterキーを使う。そのまま閉じちゃうとキャンセル扱いになる。
 

 これで中央位置への拘束の設定は完了。

 だけど、元々のボタンの位置が画面中央でなければワーニングマーク(注意だけど致命的じゃない)が出ます。

 

 

 これは「中央位置への拘束が指定されたけど、いまのボタンの位置って中央じゃないっすよ」って注意。この場合、選びうる選択肢としては

 

 1、画面パーツの位置を、指定した制約の位置になるよう修正

 2、制約側のパラメータを、今の画面パーツの位置になるよう修正

 3、制約破棄

 

があります。今回の場合は中央配置させたいので選ぶのは1ですね。

 さっきの注意マークをクリックし、表示された問題点リストの項目横にあるワーニングマークをクリックしましょう。

 修正方法の候補リストが表示されます。

 

 一番上のUpdate framesが「画面パーツの位置を指定した制約の位置になるよう修正」っす。これで注意は消え「地図」ボタンは、どのiPhone、iPadでも画面の中央に配置されるようになる。

 Swiftで記述する場合のめんどくささが嘘のよう。

 しかも、横置きとか縦置きとか、実行するiPhoneやiPadの種類も選べて確認できるようになってもいます。ビバ、ストーリーボード!

 

 

 ちなみに拘束(NSLayoutConstraint)はoutlineビューでは項目、canvasビューでは拘束線として表示されます。Tap Gesture Recognizerの時と同じように選択、deleteキーで削除したりもできる。選択してAttributesインスペクタで拘束パラメータを変更ってなこともできますよ。

 

 

 いろいろ試してみてちょ。

 

サンプル:

 http://tetera.jp/xcc/book-sample/storyboard-4.zip

 

 シミュレータで実行でもよろしあるよ。