Swiftで遊ぼう! - 278 - ポップオーバー(popover)の考察
- Swiftで遊ぼう!の前書き-> Life-LOG OtherSide
- 初心者はここから!-> 50オヤジでもできるiOS開発
- 私の本業、オフィシャルなブログ-> Life-LOG
- Swift 3 対応
2017年5月1日:イメージを改訂
2017年5月10日:内容をまとめました
Segueで新しいMVC、ポップオーバーを作る説明に入ります。
Main.storyboard上にあるHappinessViewControllerの横に新しいUIViewControllerを設置(オブジェクト・ライブラリからドラッグ)します。このポップオーバーはsmiliness値の履歴を並べるだけです。いままでのように新しいUIViewControllerを継承したTextViewController.swiftを作成してから変更します。
TextViewController上にText Viewもオブジェクト・ライブラからドラッグ&ドロップして配置します。HappinessViewControllerにNavigationControllerバーを設置してBar Button Itemもオブジェクト・ライブラからドラッグ&ドロップしてから「History」に改名します。ジェネリックなSegueを作るためにこのボタンからTextViewControllerに「Ctrl + ドラッグ」します。次のポップアップウインドウが出現します。
「Present Ad Popover」を選べばストーリーボード上にSegueができます。
そしてSegueで重要なプロパティは2つ、identifierとdestinationViewControllerです。
ここでSegueを選んでアトリビュート・インスペクタを選び、ユニーク値のidentifierを設定します。
ここでは「Show Diagnostic History」とします。
基本的にポップオーバーも普通の画面遷移の実装と同じようにできます。ストーリーボードで「Ctrl + ドラッグ」すればコードを書かなくてもセグエを作成できます。
override func prepare(for segue:UIStoryboardSegue, sender:Any!) { if let identifier = segue.identifier { switch identifier { case History.SegueIdentifier: if let tvc = segue.destination as? TextViewController { tvc.text = "\(diagnosticHistory)" default: break } } }
Segueを使ってViewControllerを生成させるためにprepare(segue: sender:)をオーバーライドして実装するのが基本です。普通の画面遷移と同じ手順でTextViewController上のtextを表示させます。
このコードの中で、History.SegueIdentifierとdiagnosticHistoryにはちょっとしたプログラミングテクニックが使用されています。ユーザー定義の定数をまとめたり、NSUserDefaultsを使って履歴をまとめているのですが説明を省略します*1。
これをiPadのシュミレーターで走らせると次のようになります。
なんとなく上手くいっているように見えます。しかし、これをiPhoneシュミレーターで走らせると
iPhoneでは完全に画面を占拠するModalタイプのSegueになってしまいます。
ここでサイズを変更するために少し工夫が必要になります。
ViewControllerにコードされたPopoverはiPhoneでモーダル(Modal)、画面全面を覆い尽くすViewを表示してしまうため、画面外をタップしてキャンセルすることができません。
そこでiPhoneの小さな画面で小さなポップオーバー画面を表示させるために工夫が必要になります。それは、「UIPopoverPresentationControllerDelegate」プロトコールのメソッドを使用するということです。
私の理解した範囲で解説すれば、UIPopoverPresentationControllerクラスを使うために外部で利用(デリゲーション)するためのプロトコールUIPopoverPresentationControllerDelegateが用意されています。すると、UIPopoverPresentationControllerクラスの内容を知らなくても利用できるということになります。
ということで、上のコードの中枢部分にあたる次のコード
if let tvc = segue.destinationViewController as? TextViewController { tvc.text = "\(diagnosticHistory)" }
に次のようにコードを入れていきます。
if let tvc = segue.destination as? TextViewController { if let ppc = tvc.popoverPresentationController { ppc.delegate = self } tvc.text = "\(diagnosticHistory)" }
UIPopoverPresentationControllerクラスのプロパティにdelegateがあり、ここに自分自身を持たせるということです。これでUIPopoverPresentationControllerを操作することができるのですが、プロトコールとデリゲーションの関係で説明した>ステップ5になります。
iOSフレームワークの利用は、プロトコールが基本というのはなんとなく理解できているので、「Swiftで遊ぼう! - 260 - プロトコールとデリゲーション ProtocolsとDelegation」の手順で実装します。
クラス宣言のところでプロトコールを組み込むのがステップ4です。
class DiagnosedHappinessViewController: HappinessViewController, UIPopoverPresentationControllerDelegate {
ということは具体的に操作するためのステップ6が必要です。
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle { return .none }
これでランをさせると次のようになります。
それにしてもポップオーバーのサイズが大きいのでこの調整をしていきます。
本来ならデリゲート(デリゲートされた先*2の具体的な実装となる次のコードも基本的に全然わからないので調べないといけません。
}
このデリゲーションメソッドの戻り値のUIModalPresentationStyleですが、どうもStyleというキーワードが含まれているものはenum型というのがお決まりのようですね。ちょっと調べると、
enum UIModalPresentationStyle : Int { case fullScreen case pageSheet case formSheet case currentContext case custom case overFullScreen case overCurrentContext case popover case none }
色々あるけど、.none以外はすべて画面をすべて覆い尽くします。
またポップオーバー画面のサイズをコンテンツに合わせる方法は、preferredContentSizeを使うので、TextViewControllerの中でオーバーライドします。
override var preferredContentSize: CGSize { get { if textView != nil && presentingViewController != nil { return textView.sizeThatFits( presentingViewController!.view.bounds.size) } else { return super.preferredContentSize } } set { super.preferredContentSize = newValue } }
ViewControllerが持っているプロパティの書き換え(overriding)で変更すればいいんです。なんとなく分かるような分からないような(^_^;) それでもこのようなコーディングに慣れていく必要がありますね。
これでラン(Comd + R)させると
これでサイズが調整されました。
考察になっていないのですが、先に進むためLecture7を終わりにします。