ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
SwiftUIを統合する
SwiftUIは、任意のAppleプラットフォームの既存のコードベースと組み合わせて使えるように設計されています。このセッションでは、SwiftUIのビューをAppの階層に追加することであらゆるAppleプラットフォームにSwiftUIを導入し、既存のデータモデルなどを活用する方法を紹介します。
リソース
関連ビデオ
WWDC19
-
ダウンロード
(音楽)
(拍手)
こんにちは タヌ・シンハルです 同僚のローリと SwiftUIの統合について話します
まず始めに 既存のアプリケーションに SwiftUIを加えるお手伝いをします 次に UIKitやAppKit WatchKitのビューをSwiftUIに― 埋め込む方法を学びます 第3に SwiftUIのために データモデルを設定する 戦略についてお話しします 最後にSwiftUIのドラッグ&ドロップや ペーストモード― フォーカスシステムの使い方を 学びます それにより アプリケーションが システム全体と統合します
SwiftUIのコンテンツを アプリケーションで ホストするのは簡単です ホスティングコントローラを 使います
ホスティングコントローラでは SwiftUIをビューコントローラや― インターフェイスコントローラの コンテンツとして設定できます
ホスティングコントローラは 3つのフレームワーク全部にあります
1つ目はUIHostingController
これはUIViewControllerの サブクラスです rootViewという1つのパラメータを取る イニシャライザがあり ここにSwiftUIビューを渡します
ホスティングコントローラが 初期化されると コードやStoryboardで他の ビューコントローラ同様に表示されます
Macには NSHostingControllerがあります SwiftUIのコンテンツを NSViewControllerで 表示するために使うものです
Macのデベロッパなら ビューコントローラ全体を 表示させたくないかもしれません 小さなSwiftUIビューを AppKitビュー階層に 埋め込みたいと思ったら?
そのために NSHostingViewがあります NSHostingViewは NSViewのサブクラスで SwiftUIビューをAppKitビュー階層に 直接 表示するために使います
Auto Layoutを使うと SwiftUIビューのコンテンツサイズの プリファレンスを自動で選びます 思いどおりの レイアウトになるのです
Auto Layoutは コンテナビューを使って― UIHostingControllerや NSHostingControllerを― 既存のAppKitやUIKitのビュー階層に 埋め込む時にも有効です
watchOSには WKHostingControllerがあります 使うためには まずサブクラスを作ります サブクラスでSwiftUIビューを ボディから返します 次にStoryboardで インターフェイスコントローラか ホスティングコントローラを選択
アイデンティティインスペクタから クラスを設定します WKHostingControllerの カスタムサブクラスとしてです 他のインターフェイスコントローラと 同様に使えるようになります WatchKitの側で生じた変更のために ボディを無効にする必要がある時は メソッドのsetNeedsBodyUpdateと updateBodyIfNeededを使います
SwiftUIコンテンツを 動的なインタラクティブ通知で 使うのも簡単です
WKUserNotification HostingControllerを使い 再びボディから SwiftUIビューに返します ボディはdidReceive通知メソッドが 呼び出されるたびに更新されます
SwiftUIを使って Watch Appsを構築する方法は “SwiftUI on watchOS”の セッションをご覧ください
ホスティングコントローラが必要な アプリケーションの例を紹介します
先日 植物の購入を考えて― 植物や樹木の勉強をしました それをカタログ化する アプリケーションを作るために―
UIKitを使って構築を始めました これはUIKitの標準的な テーブルビューコントローラです
それから SwiftUIのことを学び SwiftUIを使って 詳細ビューを作りました UIKitViewControllerから SwiftUIにナビゲートするのに 加えたコードはありません 設定方法を実演してみます
SwiftUIビューです 詳細ビューを表示するために 使っています テーブルをタップした時に 導きたいのはこのビューです Storyboardを見ましょう
Storyboardのライブラリで ホスティングビューコントローラを 探します Storyboardにドラッグしましょう
テーブルでセルを選びます コントロールキーを押して ホスティングコントローラにドラッグ Show segueを選びます ホスティングコントローラに コンテンツを追加しましょう テーブルビューコントローラを 開きます
パネルを隠して 空間を作りましょう
作ったばかりのsegueを 選択します コントロールキーを押し ビューコントローラコードにドラッグ IBSegueActionが作られました
IBSegueActionは Xcode 11で新たに導入され Storyboardのsegueを ビューコントローラのコードと つなぎます デスティネーションの ビューコントローラで 直接 プロパティを設定でき― prepareForSegueメソッドを 使う必要がありません (拍手) ありがとう
Storyboardを閉じて コードに集中しましょう
SwiftUIビューの インスタンスを作ります rootViewはSwiftUIビューの PlantDetailsViewになっています このrootViewを― ホスティングコントローラの イニシャライザに渡します
UIKitTableViewControllerから ナビゲートして SwiftUIのPlantDetailsViewに たどり着けます
(拍手)
SwiftUIをアプリケーションに 追加するのは簡単だと分かりましたね 次は既存のフレームワークで 作られたビューを SwiftUIビューに埋め込む方法です Representable Protocolを 使います これを使うとSwiftUIで― UIViewsやNSViews WKInterfaceObjectを表示できます さらに SwiftUIで ビューコントローラも表示できます これにはビューコントローラの Representable Protocolを使います
Representable Protocolには 必要なメソッドが2つあります MakeメソッドとUpdateメソッドです
Makeメソッドはビューあるいは コントローラを作成し SwiftUIで表示します Updateメソッドはビューを 現行の設定に更新します 初期化の段階で まずMakeメソッドが呼び出され それにUpdateメソッドが続きます SwiftUIが更新を要求するたびに Updateメソッドは 何度でも呼び出されます
最後に提供するのがオプションの Dismantleメソッドです ビューやコントローラを 削除する前に 必要なクリーンアップコードを 入れます
これらのメソッドの Swiftの定義を見てみましょう
MakeとUpdate Dismantleメソッドは どのフレームワークでも 同様の挙動を示します AppKitとUIKitでは これらのプロトコルが SwiftUIのビューや ビューコントローラを表示するのです WatchKitではWKInterfaceの Representable Protocolで SwiftUIのWKInterfaceObjectの サブセットを表示します サポートされるWatchKitObjectsは デベロッパWebサイトで確認を
つまり Makeメソッドと Updateメソッドを使えば SwiftUIのビューを 表示することができます しかし ビューは 複雑になりがちであり 表示以上のことを 望むこともあります SwiftUIでターゲットアクションや デリゲートを出したり
SwiftUIの環境から読み込み 適宜 対応したいかもしれません
ビューにアニメーションがあるかを 知っておくといいでしょう
ビューとSwiftUIを よりよく統合するために Representable Contextを 作りました Representable Contextには 3つのプロパティがあります 1つ目がCoordinatorです ビューとSwiftUI間の 連携を助けます Coordinatorは一般的なパターンの 実装に使われます デリゲートやデータソース ターゲットアクションなどです 次のプロパティはEnvironmentです SwiftUIの環境を 読み込むのに使います 例えばカラースキームやサイズ 方向などの― システム環境です あるいは アプリケーションが定義する カスタム環境プロパティの場合も 最後がTransactionプロパティです SwiftUIにアニメーションが あるかどうかをビューに知らせます
Representable Contextが 使えるのは ビューとビューコントローラ インターフェイスコントローラです
Representable Protocolの ダイアグラムを見ましょう Makeメソッドと Updateメソッドでは Representable Contextのために パラメータを渡しました このコンテキストにはEnvironmentと Transactionの情報がすでにあります Coordinatorを使いたいのであれば 自分で作成します オプションの Make Coordinatorメソッドで可能です 初期化の段階で Make Coordinatorメソッドが呼び出され Make Viewと Update Viewメソッドが続きます これでビュー設定の際のコンテキストに Coordinatorが使えるようになります
例を挙げて説明したほうが 分かりやすいでしょう 先ほどの植物アプリケーションの 詳細ビューです SwiftUIで作成しました
UIKitを使って 別のビューも作りました これは評価です これから UIKitベースの評価コントロールを SwiftUIビューに埋め込みます
さらにSwiftUIに ラベルも追加して 評価コントロールから 評価を読み込めるようにしたい すべて UIViewRepresentable プロトコルによって可能です 設定を実演してみましょう
このプロジェクトには UIKitRatingsControlが含まれます UIKitベースのビューで 5つ星をレンダリングします SwiftUIで表示したいのは このビューです
RatingsControlRepresentationは UIKitビューのラッパーです SwiftUIでUIビューを 表示することを可能にします
UIViewRepresentable Protocolに 必要なメソッド2つも加えました makeUIViewとupdateUIViewです
makeUIViewメソッドでは UIビューのインスタンスを 作成するだけです ここではUIKitRatingsControlですね それを返します これでRatingsControlRepresentationが SwiftUIで使えるようになります プレビューを見てみましょう
RatingsControlRepresentationを SwiftUIで呼び出すと星が表示されます それが今度は UIKitベースのビューを表示します すべての星はグレーの状態です 評価に応じて色をつけるには SwiftUIから評価を読み込んで UIKitビューに設定します
すでにコードにバインディングを 追加しましたので SwiftUIから 読み込むことができます
次はupdateUIViewメソッドです UIビューで評価を設定します
すぐに星に色がつきました この評価はSwiftUIから 読み込んでいます (拍手) 先ほどのプレビューを ライブモードにします SwiftUIに クリアボタンを加えましたので 評価をゼロに更新できます これをタップすると updateUIViewメソッドが呼び出されて UIビューで評価が更新されます 星をタップして 評価を変更することもできます でも星をタップしても 右のラベルが適切に更新されません
UIビューが評価に 内在する値だからです SwiftUIに送り返さないのです これを修正するために使うのが ターゲットアクションパターンです これを実装します
ターゲットアクションパターンを 使うためにCoordinatorを加えます Coordinatorを作成しましょう これはNSObjectです 関係のある値をストアします イニシャライザを使って 関係のある値を設定します ここではratingです 次にratingにratingChangedという セレクタを追加します ターゲットアクションパターンによって 呼び出されます ここではCoordinatorのratingを UIビューから得る評価に設定します
次はmakeCoordinatorメソッドの 実装です このメソッドでは Coordinatorのインスタンスを返し RatingsControlRepresentationから ratingのバインディングを渡します
最後にmakeUIViewメソッドで このCoordinatorを使います そしてターゲットを追加
valueChangedイベントのトリガーが UIKitで実行されるたびに ratingChangedメソッドが 呼び出されるようになりました これをアプリケーションに 追加しましょう PlantDetailsViewに入り― RatingsControlRepresentationを 呼び出します UIKitが優先すべきフレームを 設定することもできます プレビューを見てみましょう
ライブモードにします
星をタップすると 値とテキストラベルが 希望どおりに更新されました
(拍手)
これでSwiftUIを アプリケーションで使えますね 最初にSwiftUIコンテンツを持つ ホスティングコントローラを作ります IBSegueActionsを使えば アプリケーションに簡単に追加できます
SwiftUIビュー階層に埋め込みたい ビューをすでに作成済みなら Representable Protocolを 確認します Representable Contextを使って さらに機能性を高めましょう 続いてローリがデータモデルと SwiftUIの統合について話します (拍手) ありがとう タヌ
僕はローリ・レデ SwiftUIとAppKitのエンジニアです アプリケーションにSwiftUIビューを 追加するのは簡単だと分かりましたね データも入れました ここではデータとの 統合について話します
タヌの実演で見せたのは 植物のデータモデルです それをSwiftUIの rootViewに渡しました SwiftUIビューが適切なプロパティを 引き出し それをレンダリングしました しかし これは 一度きりのオペレーションです データモデルは SwiftUIフレームワークの 外部にあるからです
クラウド あるいはユーザから データモデルに変更が加わると SwiftUIには伝わらず データコンテンツを変換できません
これに対処するのが BindableObjectデータプロトコルです シンプルなプロトコルで didChangeパブリッシャを 1ついじるだけです
didChangeプロパティを実装し BindableObjectに対応させます するとデータモデルを参照していた SwiftUIビューで @ObjectBindingラッパーを使えます これでSwiftUIはBindableObjectを 参照していることを認識し このビューに サブスクライブできると知ります これでデータが変更されるたびに didChangeパブリッシャが サブスクライバに データが変更されたことを伝えます そのサブスクライバの1つが SwiftUIです これでSwiftUIはデータモデルが 参照するビューを自動的に知り 更新の必要性を認識します さらにデータに $の接頭辞が使えます またデータモデルに バインディングが使えるので データモデルへのアクセスを 書き直せるようになります
テキストフィールドに変更を加えると 直ちにデータモデルが更新されます
BindableObjectプロトコルは シンプルです プロパティを1つ実装するだけです とても柔軟です どんな通知システムを使っていても データモデルに生じた変更を パブリッシャが知らせてくれます データモデルがアプリケーションの データの唯一の情報源になります
SwiftUIで重要な点です データモデルでも SwiftUIのいかなるステートでも 唯一の情報源を持たせたいのです 宣言型のビュー階層と 連携するので ビューコントローラを書き込み データを同期する手間が省けます
実演しましょう
タヌから 植物データのアイデアを聞き 興奮しました Macアプリケーション用の バージョンを作ることにしました タヌがiOSバージョンを作り 僕がMacバージョンを手がけたのです 同じくらい開発が進んだ頃 SwiftUIが発表されました SwiftUIを使えば詳細ビューが 簡単に作れることに驚きました
同じアプリケーションの Macバージョンです
左にはNSTableView 右には同じ詳細ビューがあります
さらに一歩進んで これをデータモデルと統合し 詳細ビューに変更を加えて それをデータモデルに反映させます データモデルです ご覧のとおりシンプルです
植物の配列があります ここに留意してください
植物が変わるたびに PlantsDidChange通知を出します ビューコントローラの テーブルビューがこれを知ると テーブルビューに データをリロードします すでに通知は出されているので didChange BindableObjectを 実装するのに使います
didChangeを実装しました NotificationCenterパブリッシャを PlantsDidChangeに使い 見たいオブジェクトを “self”にします もう1つだけ
データに変更を加えることを メインスレッドのSwiftUIに伝えます receive(on)演算子を使い 全サブスクライバに パブリッシャが 伝えるようにします
データモデルが収まったので 次は詳細ビューです PlantsDataModelの参照先です ここに追加します
@ObjectBindingです
ObjectBindingラッパーを使っており 向かう先はisEditingステートですが 現在はEmptyViewを使っています
でもEditablePlantsViewには すでに書き込みました ここでは$の接頭辞を使って 特定のplantインデックスに バインディングできます
アプリケーションを再起動します
変更が加わりました 編集ボタンを押すと プロパティを編集できます テーブルビューが更新されました ハイビスカスが好きなので 5つ星をつけます データモデルで直接 データを更新しました 好きな時に戻れます
(拍手)
NotificationCenterを すでに使っていたので NotificationCenterパブリッシャを 使っただけですが 他のパブリッシャもあります 例えばKey-Value Observing パブリッシャです
KVO準拠の全オブジェクトにあります KeyPath機能のためのパブリッシャを 使うことで入手できます もっと興味深い例を見ましょう
ユーザデフォルトの変更を 監視するクラスがあります
面白いのはここです 各デフォルトに パブリッシャを作ります userOption1とuserOption2 でも2つを合体させて 1つのパブリッシャにし― それをdidChangeプロパティに 適用します 変更されたのが userOption1でもuserOption2でも didChangeと統合した パブリッシャは― SwiftUIで変更の指示を出し SwiftUIがビューを更新します
パブリッシャはCombineフレームワークの 恩恵を受けています Combineフレームワークは 新しいもので― 時間とともに値を処理するための 宣言型APIです
パブリッシャに加えて マージ演算子などが 複雑なマージを行ったり パブリッシャを統合したりします
“Combine in Practice”の セッションをご覧ください もう1つ お話ししたい 特定のパブリッシャがあります PassthroughSubjectです
データモデルに関して Combineフレームワークの どのパブリッシャも不完全な場合 例で見せたように PassthroughSubjectを使えます Core Dataアプリケーションがあり NSFetchedResultsControllerを 使っていれば データベースから データスライスを得られます NSFetchedResultsControllerに デリゲートを提供して連携させます 実装すべきデリゲートメッセージは controllerDidChangeContentです データベースが 変更されると知らせます SwiftUIにも 知らせる必要があります didChange PassthroughSubject パブリッシャを選択して 手動で変更を送るよう指示します サブスクライバの1つとして SwiftUIが認識すると すべてのビューが更新されます
BindableObjectに注目しました SwiftUIの外部でも内部でもデータを 管理するツールは他にもあります “Data Flow Through SwiftUI”の セッションをご覧ください いつ どのタイプのツールを 使うべきか議論しています
データモデルとの統合以外にも SwiftUIで 考えるべきことはあります 残りのシステムとの統合も重要です iOSやmacOS tvOSの ドラッグ&ドロップや watchOSのDigital Crownなどです
アイテムプロバイダの話から しましょう Foundationフレームワークが 提供する偉大なテクノロジーです アプリケーションでデータを 様々な形で移動させられます プロセスを横断して データを送るツールでもあります アイテムプロバイダは ユニバーサル型の識別子で アイテムが表現する データの型を示します リクエストに応じて その型のデータを 提供する必要があります 実演ではメインスレッドで データを変更するように言いました アイテムプロバイダは非同期型です アイテムプロバイダからのデータは 必ず変更して メインスレッドで出力させましょう
ドラッグ&ドロップも使います onDragモディファイアを使って ビューをドラッグソースにします ビューでドラッグを始めると― クロージャを呼び出し アイテムプロバイダを提供 それがビューと関連するデータを 提供します ビューのレンダリングを自動的に行い それをドラッグイメージとして使います ドロップを受け入れるために onDropモディファイアが使えます onDropモディファイアで ユニバーサル型の識別子の配列を渡し ビューでどんなデータを受け入れるか 記述します ユーザがその型のデータを ビューにドロップすると 自動的にアクションクロージャが 呼び出され その型に適応するアイテムプロバイダの 配列が提供されます ドロップがビュー内の どこで実行されたかを教えてくれます onDropモディファイアの 別のバリアントもあります ドロップクロージャではなく デリゲートを取ります デリゲートはビューを移動する間 ドロッププロセスの可視性を与えます 例えばユーザがドロップする前に カーソル位置を得られます
ペーストボードでも アイテムプロバイダを使います ペーストコマンドを許可したい時は onPasteコマンドモディファイアを 使えます onDropモディファイアのように 許可するユニバーサル型の 識別子の配列を供給します ユーザがビューでペーストすると アイテムプロバイダの配列を提供します onPasteがonDropから 区別される点を指摘しておきます
クロージャに ロケーションパラメータがありません これがポイントです ドラッグ&ドロップの時 ユーザはカーソルや タッチロケーションで― どのビューがドロップを 許可するかをターゲットにします ペーストコマンドは間接的です メニューからペーストを選んだり キーボードショートカットを使うか iOSの新ジェスチャを使うか選びます
ペーストコマンドが向かうビューを 知るために使うのが フォーカスシステムです フォーカスシステムは 重要なツールで ユーザが多様なUIの要素を ナビゲートし― 特定されていないアクションが 向かうべき場所を知らせます
Macにおいて非常に有用です キーボード入力や メニューアクションコマンド iOSのキーボードコマンドや 新しいジェスチャを使う― コピー&ペースト 元に戻す&やり直しなど
tvOSでは特に重要です フォーカスが決めるものに Siri Remoteのボタンアクションが 実行されるべき場所があります watchOSでは フォーカスでDigital Crownイベントの 場所を決めます
ビュー階層のビューの1つが あるところで フォーカスビューになります ユーザがDigital Crownを回すような 間接的なアクションを実行する時 コマンドに合うビューモディファイアが フォーカスビューにあるか確認します この場合は Digital Crownコマンドです あれば 適切なクロージャを 呼び出します なければビュー階層をさかのぼり Digital Crownコマンドを持つ 原形のビューを探して― 適切なクロージャを呼び出します
SwiftUIは プラットフォームに合わせて ビューからビューへ移動する― フォーカスも処理します 皆さんは どのSwiftUIビューが フォーカスを得るか指定するだけです focusableモディファイアを使います テキストフィールドのような リーフコントロールを除いて SwiftUIビューはデフォルトでは フォーカスを得ません focusableモディファイアと Passthroughを使って フォーカスを得るビューを 指定します ビューがフォーカスを得る時と失う時に 呼び出すクロージャを 渡すこともできます これを使ってUIを更新し ユーザに視覚的な フィードバックを与えます
onExitやonPlayPauseなどの コマンドもあります Siri Remoteを使う時の tvOSの例です ここで話題にしたいのは onCommandモディファイアです これはObjective-Cスタイルの アクションセレクタからの― アクションを 指示するために使います 例えばfirstResponderに関連する メニューやツールボタンからです iOSでの使い方も同じです
例えば ビューが3つの メニューアイテムを受け入れられるなら 適切なセレクタを持つ 3つのonCommandモディファイアが ビューに付加されます
元に戻す&やり直しのサポートは すでに十分でしょう SwiftUIでも 同じUndoManagerを使います
アプリケーションに新しいSwiftUIを 付け加える場合はUndoManagerに― 何かする必要は ほとんどありません 元に戻すレジストレーションが データモデルに近い下層で 行われている場合は特にです しかしUndoManagerに アクセスが必要なら Environmentプロパティラッパーと UndoManager KeyPathを使います
SwiftUIはSwiftベースのAPIですが
すでにObjective-Cコードが たくさんあります Objective-CとSwiftは すでにうまく統合されています SwiftUIをObjective-Cコードと 一緒に使えるのです 標準的なObjective-CとSwiftの 統合のルールが適用されます SwiftUIでは ホスティングコントローラや ホスティングビューを Swiftでラップする必要があります
ここではホスティングコントローラを ラップします UIViewControllerの サブクラスになります Objective-C属性を与えます これで Objective-C実装ファイルから Swiftクラスを インスタンス化できます
Swift実装において UIHostingControllerを インスタンス化して SwiftUIのrootViewに渡します
UIViewControllerは Objective-C実装ファイルから使います それを表示しています
データモデルもラップします
それでBindableObjectプロトコルを 実装できます これはシンプルなSwiftクラスです NotificationCenterパブリッシャを使い BindableObjectプロトコルを実装します これは例です ObjCDataModelが NotificationCenter通知を出していれば ObjCDataModelを参照するだけです WrappedHostingControllerが ObjCDataModelに ポインタを渡します ホスティングコントローラで WrappedDataModelを作成し ObjCDataModelにアサインして WrappedDataModelを SwiftUI rootViewに渡します この2つのラップされたクラスが SwiftUIとObjective-Cコードを シームレスに統合します
今日からでもアプリケーションで SwiftUIを使い始められますね 新しいUIを追加するなら SwiftUIを使ってください
明日11時からラボがあります SwiftUIを試してみましょう 問題があれば言ってください 喜んで相談に乗ります 皆さんのアイデアを 楽しみにしています ありがとうございました (拍手)
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。