【Swift】Container Viewの使い方。ドラッグでコンテナビューを出し入れする。(Swift 2.1、XCode 7.2)
コンテナビューの使い道
前回の記事で、Container View(以下、コンテナビュー)を使って画面を2つに分割し、別々に画面遷移をさせる方法について説明した。⇒「前回記事」
コンテナビューの画面分割以外の使い道としては、画面の上に別の画面を重ねるようなものが挙げられる。
普通の画面遷移であればビューコントローラーをセグエで接続すればいい話ではあるが、iOSの通知センターのような、画面の外から別の画面をドラッグ&ドロップで指で運んで出し入れするような画面を作りたい場合にコンテナビューを使うと作りやすい。
コンテナビューを出し入れするものを作る
「Screen Edge Pan Gesture Recognizer(以下、エッジパンリコグナイザー)」の記事で、普通のViewを画面上端から引き入れるものを実装した。この動きをコンテナビューを使って実装してみよう。
iOSの通知センターのように、画面上端を下方向にドラッグすると曇りガラスの画面が現れる。曇りガラスが画面全体を覆ったあとに、画面下端を上方向にドラッグすると曇りガラスの画面が上に移動し始めるようにする。
「コンテナビューではなく、普通のViewを使えばいいじゃないか。」
と思うかも知れないが、コンテナビューを使うとストーリーボード上が2つのビューコントローラーになるため、部品の配置やコーディングを2つの画面で分けて実装できるので作りやすい。
以降の手順を開始する前のXcodeプロジェクトをGitHubに置いたので、実装を試してみる人はご利用されたし。
⇒「テスト用プロジェクト」
事前準備ではストーリーボードに「青空画面のビューコントローラー」と、「Visual Effect Viewを適用した曇りガラス画面のビューコントローラー」の2つを配置した。
画面端をドラッグしたイベントを処理するために、青空画面の画面上端、曇りガラス画面の下端にそれぞれエッジパンリコグナイザーを適用しておいた。これにコンテナビューを追加し、曇りガラス画面を指で出し入れできるようにする。
2つのビューコントローラー間の値の受け渡しにはNotification Center(以下、通知センター)を利用する。
コンテナビューをデバイス画面に配置する(下図赤矢印)。コンテナビューと一緒に追加されたビューコントローラ(黄緑枠)を選択してDeleteキーで削除する。
Ctrlキーを押しながらコンテナビューをドラッグ&ドロップで曇りガラス画面まで運んで吹き出しのメニューを表示し、「Embed」を選択する。これでコンテナビューのビューコントローラーに曇りガラス画面が使われるようになった。
コンテナビューを選択した状態で、下図赤枠のPinボタンを押して吹き出しの設定画面を表示する。Constrain to Marginsのチェックを外し、黄緑枠の下左右の距離の制約をすべて「0」にする(上は入力しない)。下の制約については紫枠の矢印を押して「View」からの距離に変更すること。
続いて、Heightにチェックを入れて、「Add 4 Constraints」ボタンを押す。Heightの値はアプリ起動時にソースコードから変更するので、値は何でも構わない。これでコンテナビューの距離と大きさの制約が追加された。
下図赤枠のアシスタントエディタボタンを押してViewController.swiftを開く。Ctrlキーを押しながら、黄緑枠の制約「height=XXX」をドラッグ&ドロップでソースコードまで運んで吹き出しの設定画面を表示させる。Connectionに「Outlet」、Nameに「heightConstraint」を入力し、Connectボタンを押す。
同じようにして、紫枠の制約「bottom = Container View.bottom」の吹き出しの設定画面を表示し、Connectionに「Outlet」、Nameに「bottomConstraint」を入力し、Connectボタンを押す。
これで高さと距離の制約をソースコードで操作できるようになった。
ViewController.swiftを以下のコードに変更する。
「画面端ドラッグ時の呼び出しメソッド」が呼び出されたら、指とコンテナビューの下端の位置が同じになるように制約を変更している。指を離したときのY座標が画面半分より上の場合はコンテナビューを画面外に移動し、画面半分より下の場合はコンテナビューを完全に画面に被せている。
コンテナビューの下端をドラッグしたイベントの受け渡しは通知センターを利用している。通知センターに「コンテナビュードラッグ時の呼び出しメソッド」を登録し、メソッドが呼び出されたら「画面端ドラッグ時の呼び出しメソッド」を流用して、コンテナビューを移動している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
// // ViewController.swift // import UIKit class ViewController: UIViewController { @IBOutlet weak var heightConstraint: NSLayoutConstraint! @IBOutlet weak var bottomConstraint: NSLayoutConstraint! //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() //コンテナビューの制約を画面サイズに合わせる。 bottomConstraint.constant = view.frame.size.height heightConstraint.constant = view.frame.size.height //画面表示を更新する。 self.view.layoutIfNeeded() //通知センターにメソッドを登録する。 NSNotificationCenter.defaultCenter().addObserver(self, selector: "closeContainer:", name: "panContainer", object: nil) } //画面端ドラッグ時の呼び出しメソッド @IBAction func panTopEdge(sender: UIScreenEdgePanGestureRecognizer) { //指の位置をもとにコンテナビューの制約を更新する。 bottomConstraint.constant = view.frame.size.height - sender.locationInView(view).y heightConstraint.constant = view.frame.size.height //画面表示を更新する。 self.view.layoutIfNeeded() //ドラッグ終了時の処理 if(sender.state == UIGestureRecognizerState.Ended) { if(bottomConstraint.constant > view.frame.size.height/2 ) { //指の位置が画面高さの半分より上の場合はコンテナビューを画面外に戻す。 bottomConstraint.constant = view.frame.size.height } else { //指の位置が画面高さの半分より下の場合はそのままコンテナビューを下げる。 bottomConstraint.constant = 0 } //アニメーションさせる。 UIView.animateWithDuration(0.8,animations: { self.view.layoutIfNeeded()},completion:nil) } } //ステータスバーの表示要否メソッド override func prefersStatusBarHidden() -> Bool { //ステータスバーは表示しない。 return true } //コンテナビュードラッグ時の呼び出しメソッド func closeContainer(notification:NSNotification) { let sender = notification.object as! UIScreenEdgePanGestureRecognizer //コンテナビューを移動する。 panTopEdge(sender) } } |
TestViewController.swiftを以下のコードに変更する。
「画面下端ドラッグ時の呼び出しメソッド」が呼び出されたら、通知センターを使ってViewControllerクラスにイベントを伝えている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// // TestViewController.swift // import UIKit class TestViewController: UIViewController { //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() } //画面下端ドラッグ時の呼び出しメソッド @IBAction func panBottomEdge(sender: UIScreenEdgePanGestureRecognizer) { //通知センターに通知する。 NSNotificationCenter.defaultCenter().postNotificationName("panContainer", object: sender) } } |
以下は実際のプレイ動画