 
      これは、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アプリケーションにあります。