
これは、iPadおよびiPhone上のiOS 9のSplit View ControllerおよびPopoverの適応動作に関連するチュートリアルの 2番目の部分であり、 Size Classesコンセプトによって可能になりました。 トレーニングは、クラウドベースの写真ストレージサービスであるFlickr.comサーバーと連携する実用的なアプリケーションをSwiftで作成することで構成されています。
最初の部分は、 マスターの複雑さが異なる、Adaptive Split View ControllerとPopoverを使用する開発者の観点からの5つの興味深いケースをリストしています。 詳細はどこでも同じ-Navigation Controllerに挿入され、写真画像を表示するように設計された唯一のImage View Controller :
1.クラシックバージョン: マスターの 1つの要素、 Navigation Controllerに挿入(多くの場合、これはTable View Controllerです )
2. Navigation Controllerに挿入された多くのTable View Controllerアイテム
3. マスターとしてのTab Bar Controller
4.異なるサイズクラスのデバイスの異なるUIおよび異なるユーザークラスのケースはここでは考慮されませんが、アイデアは「iOS 9の2つのストーリーボードを備えた適応インターフェイス」で見つけることができます。
5.アダプティブポップオーバー
最初の部分では、Swiftで基本的な実験アプリケーションを作成し、ケース1〜2に拡張しました。 この記事では、実験アプリケーションをさらに複雑にし、ケース3および5に拡張します。すべてのオプションのコードはGithubにあります。
3.マスターとしてのTab Bar Controller
アプリケーションを改善し、
NSUserDefaults
ストレージ内の「最近表示された」写真を記憶する(つまり、このアプリケーションの起動時に絶えず保存する必要がある)ユーザーに別のタブBar Controllerで表示する機会を提供します。 これを行うには、以前のAdaptiveSplitViewController2Swiftアプリケーションに基づいて、新しいAdaptiveSplitViewController3Swiftアプリケーションを作成し、 Tab Bar Controllerをストーリーボードに追加し、それに付随するView Controllerを削除し、代わりにNavigation Controllerに挿入されたカメラマンと「最近の」ものを表示するように設計された別のResents画面フラグメントを接続します写真とNavigation Controllerにも挿入:

アプリケーションを起動し、iPadですべてが正常に動作することを確認します。 コンパクト幅モードのすべてのiPhoneでは、
ImageViewController
を使用した画像の画像がタイトル付きで画面に表示されますが、ナビゲーションバーや追加のボタンは表示されません。 しかし、最も悲しいことは、この画面からどこにも移動できないことです。

SplitViewControllerはDetailのモーダルビューを提供するTab Bar Controllerを介してMasterを制御するため、 SplitViewControllerは
ImageViewController
をMasterのNavigation Controllerのスタックに配置できないことを示唆しています。 この状況を修正するために、 マスターの Navigation Controllerスタックに
ImageViewController
し、 コンパクト幅モードの
showDetailViewController
デリゲートの
showDetailViewController
メソッドでこれを実行できます。
まず、 コンパクト幅モードを定義し、 Masterの Navigation Controllerを取得します。 次に、
ImageViewController
を抽出し、それをMasterの Navigation Controllerスタックに入れて、画面に表示します。
AppDelegate.swift

true
を返すということは、
SplitViewController
が
SplitViewController
制御してはならないことを意味します。 これで、 コンパクト幅デバイスの写真の画像が正しく表示されます。NavigationControllerのように戻るボタンとスライドを使用すると、モーダルに表示されずに離れることができません。

しかし、
ImageViewController
をMasterの Navigation Controllerスタックに追加したので、ランドスケープモードに切り替えるときにMasterで
ImageViewController
を取得できます。もちろん、これは受け入れられません
ImageViewController
はDetailの代わりにのみ表示されるためです:

したがって、ランドスケープモードに切り替えるときは、 マスターの Navigation Controllerスタックから
ImageViewController
を削除する必要があります。
AppDelegate.swift

削除し
imageURL
ばかりの
ImageViewController
の
imageURL
の知識を使用して、写真のリスト内の対応する行を構成できます。
AppDelegate.swift

ストーリーボードから詳細を復元し、結果の写真でMdelを構成します。
AppDelegate.swift

その結果、iPhone 6以降のポートレートモードからランドスケープモードへの移行など、iPhoneの状況は回復します。

「Resents」タブ、いわゆる「最近の」写真について少し話しましょう。 このビューコントロール rは、ユーザークラス
ResentsTVC
によって提供されます
ResentsTVC

ResentsTVC
クラスは
ResentsTVC
クラスを継承し、その唯一の機能は
NSUserDefaults
から写真のリストを読み取ることです。
ResentsTVC.swift

コードは2行だけで完了しました。以下に、なぜこれほど単純なのかを示します。
NSUserDefaults
相互作用
NSUserDefaults
、特別な
ResentsDefault
クラスと計算された
var resentsPhotos
プロパティを使用して編成されます。
ResentsDefault.swift

これは、
String
型のキーと
String
型の値を持つ辞書の配列です。 さらに、このプロパティの
{get}
NSUserDefaults
からデータを読み取り、
{set}
NSUserDefaults
書き込み
NSUserDefaults
。
NSUserDefaults
は本質的に、アプリケーションの実行間でプロパティリストデータを永続的に保存するための非常に小さなデータベースであることに注意してください。 Flickr.comサーバーから受信した写真に関する情報を格納するための内部データ構造
NSDictionarys
、
Photo
が
struct
であり、
NSDates
、
NSStrings
、
NSDates
、
NSDictionarys
いずれでもない
[Photo]
配列を選択しました。
NSUserDefaults
保存に
[Photo]
を使用することはできません。これはプロパティリストではないためです。
辞書の配列
[[String : String]]
を使用して、 プロパティリストデータである
NSUserDefaults
にFlickrの写真に関するデータを格納しますが、もちろん
Photo
を
[[String : String]]
に変換する必要があります。
DataModel.swift

NSUserDefaults
との相互作用を
ResentsDefault
クラスには、 パブリックAPIとして2つのプロパティがあります 。
var addedPhoto = Photo?
NSUserDefaults
に追加する写真、
var resentsPhotos: [[String : String]]
-
NSUserDefaults
保存されている写真に関する情報を含む辞書の配列:
ResentsDefault.swift

NSUserDefaults
ストレージでの作業は、
addedPhoto
を既存のストレージに追加するという事実から始まります。これは、1行のコードと
resentsPhotos
ヘルパー関数で行われます。この関数は、
NSUserDefaults
で
NSUserDefaults
に保存された写真
resentsPhotos
写真の
resentsPhotos
配列を追加し
NSUserDefaults
:
addPhoto (photo, inDefaultsPhotos: resentsPhotos)
resentsPhotos
を補助関数の入力パラメーターとして使用すると、この計算されたプロパティに対して
{get}
トリガーされ、
resentsPhotos
NSUserDefaults
ストレージからデータが読み取られます。 次に、
resentsPhotos
配列の最初に、新しい写真
photo
を最も「最近」として追加します。
ResentsDefault.swift

結果の配列は
resentsPhotos
再び割り当てられ
resentsPhotos
:
resentsPhotos = addPhoto (photo, inDefaultsPhotos: resentsPhotos)
、つまり
{set}
がこれに対して機能することを意味し、計算されたプロパティと更新された配列が
NSUserDefaults
リポジトリに書き込まれます。
NSUserDefaults
メカニズム
NSUserDefaults
非常に少量のデータを保存するように設計されているため、
ResentPhotoAmount
定数で指定された写真の数(この場合は20)のみに制限します。
NSUserDefaults
ストレージで写真情報を記録するのに最適な場所について考えてみましょう。 ユーザーが写真を選択した場合、その情報を
NSUserDefaults
に入れる必要があります。これは
prepareForSegue
クラスの
prepareForSegue
メソッドで行うのが最善
prepareForSegue
が、このクラスはより一般化したままにします(他の目的で必要になる場合があります)。 したがって、
PhotosSavedNSUserDefaults
クラスのサブクラスである新しい
PhotosSavedNSUserDefaults
クラスの
NSUserDefaults
に写真情報を記録することに関連する新しい機能を投稿します。
PhotosSavedNSUserDefaults.swift

ストーリーボード上のFlickr写真画面フラグメントのカスタムとして、新しい
PhotosSavedNSUserDefaults
クラスを正確に設定する必要があります。

しかし、
ResentsTVC
クラスに戻り、「最近の」写真とResents画面の断片を提供します。 そのタスクは、
NSUserDefaults
から「最近の」写真に関する情報を回復することであり、このために1行のコードを使用しました。
ResentsTVC.swift

この行を、
JustPostedFlickrPhotosTVC
からも継承する別の
JustPostedFlickrPhotosTVC
クラスの同じ行と比較すると:
JustPostedFlickrPhotosTVC.swift

次に、同じ
.flatMap(Photo.init)
コンストラクトが表示されます。 最初のケースでは
NSUserDefaults
から回復するときに
[String : String]
入力され、2番目のケースではFlickrサーバーからデータを読み取るときに
[String : AnyObject]
ます。 コンパイラーは、実行する
Photo
イニシャライザーをどのように知るのですか? 結局のところ、それらの2つがありますか?
DataModel.swift

Swiftでは、
struct
構造体は、入力パラメーターが異なる場合のみ、複数の初期化子を持つことができます。 私たちの場合、そのうちの2つがあります。
-
init?(json:[String:AnyObject])
、FlickrサーバーからのJSONデータからstruct Photo
を初期化するには、 -
init(userDefaults:[String:String])
、NSUserDefaults
リポジトリーからstruct Photo
を初期化します。
ここでは、入力パラメーターのタイプに応じて、タイプの推論と関数のオーバーロードのメカニズムが機能します。 したがって、
struct Photo
正しい初期化子
struct Photo
呼び出されます。
結論: Table Bar ControllerをMasterとしてSplit View Controllerの適応動作を保証するには、
showDetailViewController
デリゲートの
separateSecondaryViewControllerFromPrimaryViewController
showDetailViewController
および
showDetailViewController
メソッドを使用する必要があります。
コードはGithub - AdaptiveSplitViewController3Swiftアプリケーションにあります。
4.レスポンシブポップオーバー
以前は、 PopoverはiPadでしか表示できませんでしたが、iOS 8以降では、いわゆる「適応」バージョンのポートレートモードとランドスケープモードの両方でiPhoneにも表示されます(詳細は後述)。 Popoverの概念は変わりません
UIViewController
内に表示されるコンテンツとしてView Controllerが必要ですが、 Popover自体は
UIViewController
はありません。 これは、いわゆるPopover Presentation Controllerメカニズムを使用して画面に表示されます。
実験用アプリケーションを引き続き使用し、ストーリーボードに別の画面フラグメントを追加します。これにより、選択した写真の画像のURLが Popoverの 「ウィンドウ」に表示されます。 これを行うには、 AdaptiveSplitViewController2Swiftアプリケーション(多数のTable View Controllerをマスターとする )に基づいて、新しいAdaptiveSplitViewController4Swiftアプリケーションを作成し、左側の「URL」ボタンをImage View Controller画面フラグメント(写真の画像を表示)のナビゲーションバーに追加し、タイプがPopoverとして表示します。 これで、ユーザーインターフェイスは次のようになりました。

Popoverに関しては、 MVC自体ではありませんが、 PopoverとしてPresentタイプのセグエを使用して、 表示するView Controllerの外観をトリガーすることは興味深いです。
Present as Popoverタイプのセグエを作成するには 、通常どおり、 CTRLをView Controllerにドラッグします。また、
prepareForSegue
メソッドを実行することもできます。
タイプPresents of Popoverの セグエをインストールしたら、

「ウィンドウ」のサイズ、つまりトップビューのサイズを調整することが可能になります 。

セグエの識別子「Show URL」を設定し、
ImageViewController
prepareForSegue
メソッドを追加し
ImageViewController
。
ImageViewController.swift

Popoverの セグエを準備するときは、いくつかの追加機能に注意する必要があります。 その1つは、
prepareForSegue
内で
UIPopoverPresentationController
と呼ばれるものを取得し、 Popoverプレゼンテーションを構成できることです。 たとえば、 Popoverを何かの左側に 「ポップアップ」させたくないが、 Popoverを常に何かの右側に 「ポップアップ」させたいと言うことができます。 すべてを管理できます。 さらに、
prepareForSegue
メソッドで、自分をデリゲートとして定義できます。
ImageViewController.swift

次に、
UIPopoverPresentationControllerDelegate
プロトコルを確認します。
ImageViewController.swift

そして、
UIPopoverPresentationControllerDelegate
.None
返される
UIPopoverPresentationControllerDelegate
デリゲートメソッドを実装することにより、適応動作を「オフ」にします。
UIPopoverPresentationControllerDelegate
、iPhoneでPopover適応を「拒否」します。
ImageViewController.swift

そして、 Popoverを iPhoneの小さな「ウィンドウ」 として表示するのに必要なことはそれだけです。 通常、この方法はブログにあります。
しかし、実験アプリケーションでは、これを少し異なる方法で実装します
ImageViewController
クラスは、写真画像URLの表示方法に依存せず、写真画像URLを表示する
TextView
を含む画面フラグメント自体は自動調整するという考え方に準拠しています。
この2番目の方法を見てみましょう。
写真画像のURLの表示は、カスタムクラス
URLViewController
によって提供されます 。
URLViewController
クラスのモデルはプロパティです
var url : NSURL?
「ライフサイクル」
viewDidLoad
メソッドと同様に、インストールされると、ユーザーインターフェイスは
updateUI()
メソッドを使用して更新されます。 このクラスでPopover構成全体を直接実行するため、
URLViewController
クラスは
UIPopoverPresentationControllerDelegate
プロトコルを確認します。

他の誰よりも先に呼び出される
awakeFromNib
“ lifecycle”
awakeFromNib
では、プレゼンテーションスタイルを
.Popover
設定し、
UIPopoverPresentationControllerDelegate
プロトコルのデリゲートとして自分自身を指定します。
URLViewController.swift

UIPopoverPresentationControllerDelegate
を使用して、 PopoverがiPhoneでどのように適応するかに影響を与えることができます。 iPadのポップオーバーは、従来の方法で「ポップアップ」します-小さなウィンドウの形。 iPhoneでは、 Popoverは小さなウィンドウではなく、フルスクリーンのモーダルウィンドウに適応します。 iPhoneの小さなもののように「ポップアップ」しません。 なんで? iPhoneの画面はずっと小さく、「ポップアップ」が本当に大きい場合、画面サイズに合わせる方法がないかもしれません。 しかし、画面全体でモーダルを想像すると、画面のサイズと正確に一致します。 Split View ControllerとNavigation Controllerが自動的に適応するように、iOSはこの適応を自動的に行います。 しかし、
UIPopoverPresentationControllerDelegate
デリゲートを使用すると、この適応に取り組むことができます。それが私たちのすることです。
iOS 9では、 Popoverがプレゼンテーションを作成しようとすると、 PopoverをiPhoneに「適応」させる方法を尋ねられます。 デフォルトでは、これはフルスクリーンのモーダルビュー(
.FullScreen
return)ですが、
UIModalPresentationStyle.None
を使用したいと言うことができます。これは、自動適応がないことを意味します。 つまり、iPhoneでのプレゼンテーションはiPadでのプレゼンテーションとまったく同じになります。 小さなPopoverにとって、これは非常に理にかなっています。
UIPopoverPresentationControllerDelegate
を返す
UIModalPresentationStyle.None
デリゲート
UIPopoverPresentationControllerDelegate
実装することにより、「適応」動作を「オフ」にすることができ
UIModalPresentationStyle.None
。
URLViewController.swift

Popoverの最後の非常に重要なことは、ポップアップウィンドウのサイズです。
MVCにぴったり合うサイズの「ポップアップ」ウィンドウが表示されたら、本当に気に入っています。 MVCのサイズは異なる可能性があるためです。 いずれにせよ、あなたは本当にそのサイズを制御したいと思います。 オブジェクト指向の世界では、システムはPopoverにあるMVCにこのMVCが好むサイズを尋ねますか? どのサイズになりたいですか? それが本当であるので、 MVC自体だけがどのサイズが優先されるかを知ることができます。 しかし、 Popoverにはいくつかの制限があるため、これは推奨事項にすぎません。 たとえば、 ポップオーバーは特定の場所でのみ画面に表示できます。画面は十分に大きくなければならず、矢印には特定の方向を指定できます。 Popoverにはこの種の制限が多くあります。
しかし、彼はまだMVCに尋ねます。
UIViewController
は特別なプロパティがあります:
var preferredContentSize: CGSize
このプロパティをオーバーライドして、適切なサイズを返すことができます。 希望のサイズが常に同じ場合は、単純に設定できます。 内容に基づいて優先サイズを計算する必要がある場合は、次のように実行できます。
URLViewController.swift

その結果、 コンパクト幅デバイスでも通常 幅デバイスと同じ小さな「ウィンドウ」が表示されます。

iPadでポートレートモードで動作するPopoverには1つの問題があります。 iPadでポートレートモードで特定の写真のURLを表示する場合、ナビゲーションパネルの左側にあるFlickr Photographersをクリックして、別の写真家と別の写真を選択します...前のURLが画面に残っていることがわかりますが、 Popoverの外側をクリックすると、 Popoverは終了します。

画像はすでに変更されており、 Popoverの外側の画像自体をクリックするまでURLは古いままです。
答えは、「URL」ボタンがある同じパネルをクリックすると、そこにあるすべてのものとやり取りすることができます。つまり、ボタンをクリックするとポップオーバーは消えません。 特に、 Flickr Photographersの戻るボタンをクリックすると、 この状況は
passthroughViews
関連してい
passthroughViews
。 ナビゲーションバー全体が
passthroughViews
一部であり、本当に悪いことは、リスト内の別の写真をクリックすると、写真の写真が更新されますが 、 URLのある Popoverは更新されないことです。 画面には引き続き古い写真のURLが含まれます 。 これは本当にひどいです。
コードでこの問題を次のように解決します。 誰かが新しい
image
インストールするたびに、私は持っているポップオーバーをすべて消します。
ImageViewController.swift

今、状況は改善されました

おわりに コンパクト幅デバイスで「小さなウィンドウ」としてPopoverを表示するには、フルスクリーンモーダルウィンドウの形式でのコンパクト幅デバイス用のPopoverの適応型自動表示をキャンセルする必要があります。 これは、
adaptivePresentationStyleForPresentationController
メソッドを使用して実行できます。
UIPopoverPresentationControllerDelegate
委任します。
UIModalPresentationStyle.None
、値
UIModalPresentationStyle.None
を返します。
コードはGithub - AdaptiveSplitViewController4Swiftアプリケーションにあります。