ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
SwiftUIのご紹介
宣言型プログラミングの世界を探求しましょう:フル機能のSwiftUI Appを一から構築する方法、そしてSwiftUIとXcode両方の力を合わせ、優れたAppをより短時間で構築する方法もお伝えします。
リソース
関連ビデオ
WWDC23
WWDC20
WWDC19
-
ダウンロード
こんにちは WWDCへようこそ
“SwiftUIの紹介” 私はジェイコブです 後ほどカイルも登場しますよ SwiftUIをご紹介するのが楽しみです Appをより早く構築する素晴らしい方法です SwiftUIについて知るには 実際に見るのが一番です SwiftUIは魔法のようですが トリックはありません Appを作りながら プロセスをご説明します どんなAppを作りましょうか カイルと私はサンドイッチが大好きです そこでサンドイッチを リストから選べるAppにしました Xcodeで構築を始めましょう
まず新しいプロジェクトを作成
マルチプラットフォームの Appテンプレートを使用します
プロジェクト名は“Sandwiches”
新規プロジェクトにはiOSとMac OS向け 両方に必要なものがすべて備わっています iOS専用アセットのグループと Mac OS専用アセットのグループです
ただ ほとんどは共有グループにあります これがApp全体の コードです 短すぎるように思えますが これだけで十分です このコードは後で使います Appビュー用の コードから始めましょう
エディタの左側にはコード 右側にはキャンバスがあり コードを視覚的に表示します SwiftUIではビュー定義は Swiftコードのみです キャンバスとコードエディタは 同じコードを異なる方法で表しています キャンバス上で何かを選択すれば コードにも反映されます コードを変更すると
キャンバスにも反映されます シームレスに連携し 自由に切り替えが可能です 詳しい仕組みを説明します キャンバスには プレビューが表示され 編集やコードの確認もできます Xcodeはコードをコンパイルして実行 プレビューを表示します プレビューもSwiftUIコードを使って 作成されています 後ほど プレビューのカスタマイズに いかに有効かお見せします それでは サンドイッチのListの セルを作成しましょう この下に別のテキストを追加して 各サンドイッチの詳細を表示します ライブラリから追加します
キャンバスにドラッグするだけです
Xcodeでは どこに置いたら どうなるかも確認できます ドロップするとプレビューが更新され 追加テキストが表示されます テキストを追加するコードも 書き加えられました
これらのビューは VStackに埋め込まれています VStackはSwiftUIの一般的な レイアウトコンテナの一つで ビューを垂直に配置します ビューを水平に配置するのが HStackです
スタックの中には ビューを自由に配置できます
このプレースホルダに サンドイッチの材料を入れます ここではハードコードされた値にします
次にテキストの横に画像を入れましょう エディタ同様 簡単にコードを編集できます HStackにビューを埋め込みます ビューをコマンドクリックして “Embed In HStack”を選択します
その作業のためのコードが追加されました これで VStackの横に 画像を追加できます
あとでアセットを追加しますが ここではSFシンボル画像を使用します
基本のセルができたので 次にセルのスタイルを設定します ビューをコマンドクリックすると プロパティを確認できます
VStackのアライメントを変更します
対応するコードが更新されます
材料のテキストを見てみましょう キャンバスを使います
コントロールキーとオプションキーと クリックでインスペクタへ行けます
フォントを小さくしましょう サブヘッドラインを使います
Xcodeでコードを編集すると SwiftUIの理解が深まります これがテキストのフォントを 設定するコードです このメソッドは“モディファイア”といって SwiftUIでビューの外観や動作を カスタマイズするのに使われます コードに別のモディファイアを追加して 文字色をセカンダリカラーに設定します
それではセルをListにしましょう セルをコマンドクリックして “Embed In List”を選びます
同じセルが5つ並んだListができます これがListにするコードです デリゲートやデータソースはなく Listのビューだけです ここにデータをつなげてみます
既存のアセットとモデルファイルを ドラッグします
このファイルには情報が入っています SwiftUIで使用するため タイプをIdentifiableにします
これでListアイテムの識別ができます IDプロパティはすでにあります
この中のテストデータは Appのデバッグに使えます
ビューに戻り データを渡しましょう
sandwichesに ビューのプロパティを追加します
プレビューでは 独自のテストデータが使用できます ここにテストデータを渡します
プレビューの上に バナーがあるのに気づきましたか プロパティの追加など 大きな変更を加えると Xcodeは更新再開の準備が整うまで プレビューを停止します このボタンをクリックすると再開できます
次にListを動かしましょう Listにsandwichesを渡します
テキストを更新してサンドイッチの名前と
実際の材料の数を表示させます
サンドイッチのサムネイル画像も 表示させましょう
セルの変化に気づきましたか? 始めのセルの高さは44ポイントでしたが 画像が大きくなると それに合わせて セルが自動的に拡張されます 追加作業は不要です コンテキストの画像は シャープに見えるので モディファイアを使って 角に丸みを付けましょう
使用可能なモディファイアが分からない場合は Xcodeライブラリで探せます
cornerRadiusモディファイアを検索し キャンバスにドラッグします
Listセルにあるビューの場合 セルが同じ定義を共有しているので モディファイアはすべてのセルに適用されます
画像にドロップして 値を調整しましょう
セルとListはよくなりましたね 次は セルをタップして サンドイッチの詳細が見えるようにします
そのためにNavigationViewで Listを囲いましょう
NavigationViewを使えば App内を移動できます iPhoneではナビゲーションバーが表示され ナビゲーションスタックに プッシュできます ビューのnavigationTitleを設定して バーに“Sandwiches”と表示させます
次にスタックにプッシュする セルを構成します それには セルの中身を NavigationLinkで囲います
NavigationLinkには プッシュ先を取り込みます ここではサンドイッチの名前を表示する テキストにします
そしてセルを NavigationLinkの中に入れます
UIが更新され すべてのセルに 詳細インジケータが表示されました SwiftUIは詳細を自動的に処理するので UIがデフォルトで正しく表示されます セルの動作確認をする時にも プレビューが便利です プレビューのプレイボタンをクリックし ライブにすると キャンバスで実際のコードを操作できます セルをタップして想定通りに 動作するか確認ができます
スワイプしてポップすれば SwiftUIによる高度な動作を確かめられます セルは強調表示されており スワイプすると自動的に解除されます
あとはListにサンドイッチの数を 表示させたいと思います コードが大きくなってきたので 単一のビューにはしたくありません セルを独自のビューに抽出し 分けることにしましょう Xcodeでは簡単な操作で行えます 目的のビューをコマンドクリックし “Extract Subview”を選択します
ビューコード全体が 新しいビューに移動 名前も付けられます “SandwichCell”としましょう
sandwichのプロパティを追加し
そのsandwichを渡します
これはすばらしい改善です SwiftUIではビューが非常に軽いので ビュー追加のために カプセル化や ロジック分けは必要ありません Listコードがスリム化されたので サンドイッチの数を追加しましょう
現在は単一のコレクションを 使用していますが これはデータ駆動型のListに最適です SwiftUIを使えば 静的コンテンツと 動的コンテンツを混在させられます 現在のコレクションを ForEachに置き換えてみます
ForEachは各アイテムのビューを作成します
データ駆動型の要素に 静的要素を追加できました ここにもう1つテキストを追加して サンドイッチの数を表示します
文字色をセカンダリカラーに変更します
テキストを中央に配置するために HStackにテキストを埋め込みます
スペーサーを追加
スペーサーは SwiftUIのレイアウト要素です ツールバーの フレキシブルスペースのように 拡張してスペースを埋めます 2つのスペーサーがスペースを分割し テキストを中央に配置します
次に詳細ビューを作りましょう 新しいビューを作ります SwiftUIビューテンプレートを使用し “SandwichDetail”と名付けます
自動的にビュー構造と プレビューコードが生成されます
サンドイッチの詳細情報を 表示するために入力として渡します
ここでもプレビューコードを使用して テストデータを渡します
それからサンドイッチの画像を 追加します
画像が表示されましたが 大きすぎます デフォルトでSwiftUIは 画像を元のサイズで表示します 画質が落ちることを避けるためです しかし今回のように サイズを小さくしたい時には resizableというモディファイアを使います
サイズは合いましたが 画像の縦横比が変わってしまいました 別のモディファイアを使って 縦横比を調整しましょう
ここで“fill”を選ぶと 画像がフレーム全体に拡大され “fit”を選ぶと 画像がフレーム内に収まります プレビューを使えば 簡単に違いを確認できます “fit”を選んで 全体を表示させましょう
次にListへ戻り セルを更新して 詳細ビューをプッシュします
SandwichDetailを作成
sandwichに渡します
プレビューをライブにします セルをタップすると 画像が表示されます
ただ ナビゲーションバーに タイトルを設定していなかったので 詳細ビューに戻って修正します
ここでもnavigationTitleを追加し サンドイッチの名前を設定します
プレビューには 表示されていませんが すぐに変更を確認したいです SwiftUIのビューの全機能は プレビューでも利用可能です 他と同じようにNavigationViewに プレビューを設定します
プレビューのナビゲーションバーに タイトルが表示されました
私がサンドイッチを選ぶ時 重要なことがあります ソースの量が適切であるかです 多すぎても少なすぎても おいしくありません この画像でソースは見えますが 適量かを知りたいと思います 縦横比をfillにすると
アップで見られます よさそうですね fillとfitを切り替えることで アップと全体を交互に表示させることができます しかしApp実行中に 切り替えたい場合は どうすればよいでしょうか それを理解するにはSwiftUIのビューを もっと知る必要があります カイルに説明を変わりましょう ありがとう ジェイコブ こんにちは SwiftUIチームのカイルです SwiftUIは他のフレームワークと 少し異なります そのため 先に進む前に まずはビューの動作について説明します SandwichDetailビューを 実装したところまでお見せしました
SwiftUIではビューは ビュープロトコルに準拠する構造体で UI Viewのように 基本クラスを継承していません つまり 保存されているプロパティを 継承しないのです スタックに割り当てられ 値で渡されます
SandwichDetailには サイズや重さだけが保存されていて 追加の割り当てや 参照カウントはありません
SwiftUIは裏で ビュー階層を積極的にまとめ レンダリング用のデータを作っています そのためSwiftUIでは 小さな単一目的のビューを自由に使えるのです
私がお伝えしたいのは SwiftUIのビューは 驚くほど軽いということです SwiftUIコードはどんどん リファクタリングしてください サブビューの抽出に オーバーヘッドがないためです SwiftUIのビューの主な役割は 従来のUIフレームワークと同じです つまり UIを定義します ビュープロトコルに必要なのは bodyのみです body自体がビューです
小さなビューを合わせて 大きなビューを構築します SandwichDetailビューでは 画像を使用しました 解像度が元のままの画像を
resizableで画面のサイズに 合わせて調整 さらにaspectRatioを組み合わせ 画像の縦横比を合わせました
ビューのレンダリングは そのbodyのレンダリングにすぎません
bodyの実装に設定したブレークポイントで デバッガが停止した場合 新しいレンダリングが必要だと 判断されたことを意味します
この通り レンダリングのタイミングは フレームワークが知っています UIの定義に加えて ビューは依存関係も定義しているからです
SandwichDetailを拡張して ユーザーがタップすれば 画像の大きさの切り替えが できるようにしましょう
まず必要なのは状態変数です 画像がズームされているかを示します
SwiftUIでは 状態変数を持つビューは ビューの代わりに 変数にストレージが割り当てられます
その状態変数に基づき フィルまたはフィットを行います ズーム時はこのように表示され ズームなしではこうなります
タップの動作だけで 2つの状態を切り替えることができるのです タップで画像が拡大されフィルになったり
縮小されフィットになったりします
何が起きたのかご説明します
SwiftUIは状態変数の 読み取りや書き込みを観測できます ズームされた箇所が bodyに読み込まれたと知っているため ビューのレンダリングが bodyに依存すると認識できます
変数の変更に伴い フレームワークは再度bodyを要求し 新たな状態値を用いて レンダリングを更新します 別のコンテンツモードで試します 従来のUIフレームワークでは 状態変数と 単純な古いプロパティを区別できません しかしその2つの違いは明確なのです SwiftUIはUIのあらゆる状態を把握します スクロールビューのオフセットや ボタンのハイライト ナビゲーションスタックの内容もです
これらは“真実のソース”と呼ばれる 信頼度できるデータから取得されます
状態変数とモデルが合わさって App全体の真実のソースを構成します
先述のとおり 縦横比を呼び出すとビューが作成されます
コンテンツモードが 単純な古いSwiftプロパティの場合の定義では
すべてのプロパティを 真実のソースまたは派生値に分類できます
ズーム時の状態変数は真実のソースです
コンテンツモードプロパティは 状態変数から派生します
SwiftUIは状態変数の 読み取りと書き込みを観測できるため 変更があっても レンダリングが適切に更新されます
フレームワークが新たなbodyを要求し レンダリングを更新すると 新たに縦横比ビューが作成されます そしてコンテンツモードや 保存済みプロパティが上書きされます
こうしてすべての派生値が SwiftUIで最新の状態に維持されます
すべての状態変数は 読み書き可能な真実のソースですが
すべての単純な古いプロパティは 読み取り専用の派生値です SwiftUIは 読み書き可能な派生値を渡すツール “binding”を発明しました 技術的にはどの定数も 読み取り専用の真実のソースとして機能します プレビューを駆動するテストデータが その一例です
先述のとおり 状態変数とモデルで構成されたものが App全体の真実のソースです 後ほど 観測可能なオブジェクトを使って SwiftUIにモデルオブジェクトの変更を 観測させます
プリミティブの違いが まだ明確でなくても大丈夫です
どのデータフロープリミティブをいつ使うべきか 直感的に分かるよう説明します まずは一歩戻って 現状を把握しましょう 先ほどの例では 従来のUIフレームワークとは全く異なり ビュー自体が持続し すべてを最新かつ 一貫性のある状態に保っていました
これは従来のUIフレームワークでは 不可能でした 従来はビューがデータを読み取るたびに 暗黙的な依存関係になりました データの変更に応じてビューを更新し 新しい値を反映させる必要があるためです
それが失敗するとバグになります SwiftUIは依存関係を自動的に管理します 適切な派生値を再計算し バグが再び発生しないようにするのです 一度に1つの依存関係を管理する だけではありません
私たちが扱っているUIは大きくて複雑です 皆が頭を抱えるほど 間違いを犯しやすい状態であり 依存関係を手動で管理するのは 非常に困難です 最善を尽くした末に Appを出荷しているものの どれもUIバグの修正が必要になりました これらの線は依存関係を表しています
すべての線を理解していても イベントハンドラのコールバックでの すべての順序で UIが一貫した状態かを 確認する必要があります
UIKitに実装された 旧バージョンの サンドイッチAppのバグを例に説明します ビューコントローラコードのスケッチです ズームすると 拡大ボタンが表示されます
このような低解像度の画像が 表示された場合でも サンドイッチのソースが適量だと 確認できました
ボタンを押すと機械学習操作が バックグラウンドスレッドに送られ 画質を向上させます
よく見えます ブラウンマスタードソースですね 1つだけ問題がありました 停止しないアクティビティインジケータが 報告されたのです
バグの原因は イベントの予期しない順序付けです
この手のバグは真実のソースを更新し UIを派生させる時ではなく イベントハンドラのコールバックで サブビューを直接変更する時に起きます
原因は すぐに思い浮かぶハッピーパスに コードを記述し アンハッピーパスを見落としたことです
問題なのは イベント数の増加につれて アンハッピーパスの数が急増することです イベントを4つ取得する際 何通りの順序があるでしょうか 4つのイベントハンドラを呼び出す順序は 24通りあります
各イベントは複数回発生し得るので 実際の数はさらに増えます ユーザーが拡大ボタンを押すとします その複雑度を管理することの難しさは 非同期コールバックの調整や 割り込み可能な アニメーションの実装をした人はご存じでしょう これらの完了ハンドラは予期せず起動しがちです
5年前の自分に対し 仕事について1つ言えることは UIプログラミングは難しいということです マルチスレッドコードの同期には 誰もが苦労します 数ヵ月かけて マルチスレッドコードのバグを修正しても その正確さに100%の自信を持てませんでした 多くのUIコードでそのような状態です ビューの欠落や場所の間違いと見なされるため UIコードの難しさが軽視されがちです 競合状態とUIの不統一に共通するのは 複雑さの根本的な原因です つまり見落としがちな順序付けです 多くのビューで 4つ以上のイベントを 処理する必要があります モデル通知やターゲットアクションや デリゲートメソッド ライフサイクルチェックポイントや 完了ハンドラなどです
12個のビューには 大まかに12の階乗分の順序が存在します つまり約5億通りです これは脳での ビッグ・オー記法のようなものです
人間の頭に一度で収まる情報量を 横線で示したとします 点線は皆さんのAppを表します
2点の差が表すものは何でしょう
それはバグです
機能を追加すると 存在し得る順序付けの数が急増し それが一定数を超えると バグを避けられない状態になります
従来のUIフレームワークを使用している時に ビューの更新を1つのメソッドに収集し 単純化する方法はご存じでしょう
これにより曲線の上昇は抑えられます メソッドが1つの場合 呼びせる順序は1つだけだからです
このパターンでは UIに存在し得るすべての状態に対し 真実のソースを定義する必要があります そして真実のソースから ビューのプロパティを派生させます
SwiftUIはこのベストプラクティスに 直接影響を受けています bodyを唯一のエントリポイントにすることで フレームワークにコード化しています
こうして複雑なケースを解決できました 従来のUIフレームワークでは このパターンの運用が難しかったケースもです 例えばサブビューの削除や ナビゲーションスタックへのプッシュ テーブルビューへの更新の実行などです
こうして ビューに加え Appやシーン bodyを含むその他の SwiftUIの抽象化が機能します
変化したUIの新しいインスタンスを 単純に取り出すこのパターンが 皆さんの脳に対応します UIの不整合を事実上排除します それではデモに戻り サンドイッチの詳細ビューを終了しましょう 次はサンドイッチを拡大する プロパティを追加しましょう
“zoomed”と呼ばれ デフォルトでは“false”になります
ビューの実装内でのみアクセス可能にするため 状態は非公開にします
ズーム時の“fill”とそれ以外の時の“fit”の 切り替えのため 縦横比のコンテンツモードで使用します
最後にズーム状態を切り替えるため タップジェスチャを追加します
ライブプレビューで確認します
モードの切り替えが可能です
ズームインすると 下に空白が表示されます SwiftUIではセーフエリアと呼ばれる場所に ビューが自動的に配置されるのです AppのUI要素は 角の丸みによりクリップされません しかし エッジからエッジまでの画像を 画面全体に拡大したい場合
モディファイアを追加するだけで セーフエリアの無視が可能です
特に下端では無視してください
ズームできましたが何かが足りません
そこで SwiftUIアニメーションを追加します withAnimationで変更を囲います
異なる状態の間をアニメーションにします
アニメーションはインタラクティブでの 割り込みも可能です いつでもタップすることができ 常に正しくアニメーション化されます
次に拡大ボタンを追加します カイルが作業したモデルでは 表示した1つの画像のみで機能しました そこで 便利な機能を追加します カイルはスパイシーなサンドイッチが好きです
サンドイッチがスパイシーかどうかを 確認するため 詳細ビューの下にインジケータを表示します
既存のサンドイッチ画像の周りに VStackを追加します
そして 一般的なモディファイアを移動し VStackに適用します
ここに画像とテキストを表示するには Labelを使用します
Labelに“スパイシー”という タイトルを入れます
アイコンも関連付けています “flame.fill”と呼ばれる システム画像を使用します
Labelにはアイコンとタイトルが 一緒に表示されます リストやメニューなどの 他のコンテキストでも使用可能です Labelの外観 間隔 サイズが 自動的に変更されます
画面の下部に背景がある 下部バナーを作りたいと思います スペーサーを追加するだけで
バナーが下に移動し 画像が上に配置されます
画像を中央に配置するには 画像の上に別のスペーサーを追加します
スペーサーは要素間のパディングを維持するため 自動的に最小サイズとなります この場合 画像がコンテナの端まで 移動できるように minLengthをゼロに設定しましょう
バナーにパディングも追加します 表示している時にスペースができるので 画面の端にぶつかりません
インスペクターをクリックすると
ビューのパディングをオンにできます
見やすくなりました フォントサイズも大きくします
テキストサイズだけでなくシンボル画像も 大きくなるので注意が必要です シンボル画像はテキストと同じフォント情報を 自動的に使用します ヘッドラインフォントを使ってみましょう
次に スパイシーさを表現するため 赤い背景にします
backgroundモディファイアを使用すると 適用先ビューの後ろに 任意のビューを配置できます これらはビューに単色の背景を与えるために 単色で使用されます
ビューの背景が赤くなったのは 小さな範囲だけです
SwiftUIでは表示サイズは コンテンツに合わせて調整されています この場合 画像とテキストは自然なサイズになり 適用したパディング用のスペースもあります スペーサーとHStackを追加することで エッジからエッジまでの拡張が可能になります
最後に文字色を黄色にして スパイシーなテーマに合わせます
スモールキャップを使用するために フォントを更新します
いいですね スパイシーなサンドイッチの場合にのみ バナーを表示したいと思います 宣言構文の“IF”を使用することで 簡単にできます
サンドイッチがスパイシーな場合は
バナーを表示します
表示を確認するには プレビューデータを変更し スパイシーでない別のサンドイッチを表示します 複数のバージョンを表示するように プレビューを設定することも可能です プラスボタンをクリックすると プレビューのコピーを追加できます
使用しているデータを更新し
様々なサンドイッチを表示することができます スパイシーなバナーを含むビューと 含まないビューの確認が可能です
編集を行う際に ビューの両方のバージョンが確認可能です
Xcodeで別のプレビューを追加するには ビューの別の具体例を追加するだけです
拡大した時に このバナーがサンドイッチ画像の 邪魔にならないように変更します ズーム時に画像を非表示にするには 条件を更新するだけです
ズームすると バナーは非表示となります
アニメーション化して フェードインとフェードアウトも可能です
別のtransitionを設定し アニメーション動作のカスタマイズができます
下端で“.move”を使用してみましょう
スライドアウト スライドインができます
アニメーションが続いている時にタップすると
アニメーションが向きを変えて戻ります すべてがインタラクティブで 常に正しい位置で終了します 詳細ビューで作成したものを確認します 表示するサンドイッチで構成されています これは親ビューによって渡される派生値です ズームを確認する状態プロパティもあります フレームワークによって永続化され 縦横比のコンテンツモードを制御します
バナーはスパイシーなサンドイッチで ズームされていない場合にのみ表示されます
スライドインとスライドアウトを行うための transitionも指定します transitionの間に 何が起きているのでしょうか? 削除するとビューは新しい位置の オフスクリーンにアニメーション化されます SwiftUIはアニメーション終了まで待機して 階層からビューを実際に削除します そして 戻った時に SwiftUIがオフスクリーンを挿入し アニメーションで元の位置に戻します アニメーションを使用し階層構造から ビューの追加や削除が簡単にできます
このアニメーションは箱から出して すぐにインタラクティブにできるのです
イベント駆動型ではなく データ駆動型の長所です 先ほどカイルが説明したイベントは アニメーション化中にも 発生する可能性があります アニメーションの開始と終了では さらに多くのイベントがあります このようなものをイベント駆動型の世界で 構築するのは非常に困難ですが SwiftUIではコードの1行にすぎません
サンドイッチのListに戻り このAppを仕上げます
ここまではマルチプラットフォームの Appテンプレートを使用した― iPhoneの作業を説明しました 他のプラットフォームでの作業もご紹介します 実行先をiPhoneからiPadに切り替えます
そして ライブにします
SwiftUIはナビゲーションビューを 分割ビューに変換しました 左側でサンドイッチを選ぶと 右側に表示されます
サンドイッチを選択していない場合 空白エリアしか表示されないため サンドイッチの選択を可能とする プレースホルダーを示すには ナビゲーションビューに2つ目のビューを 追加するだけです
複数のビューを スタックに追加することができます
それらのビューをスタックするかわりに ナビゲーションビューが付与されます
この場合 最初のビューが左側に表示され 2番目のビューが 右側のビューのプレースホルダになります iPhoneでは プレースホルダは自動的に削除されます
次にMac OSを見てみましょう
iPadと同じプレースホルダが 表示されています
すべてのAppleプラットフォームで 同じビューコード モデルコード― そして アプリコードを使用できます また プラットフォーム特有の改善を 加えることも可能です
サンドイッチのListを変更可能とするため 編集サポートを追加し データモデルをもう少し現実的なものとします 現在 Appのデータは 完全に静的です サンドイッチの配列を作りましたが この配列は常に必要となります モデルを更新し サンドイッチを含む ルートストアオブジェクトを作成し― 時間の経過とともに 変更できるようにします
サンドイッチストアを使って 事前に作成された モデルファイルをドラッグします
サンドイッチを販売する場所ではなく データストアのようなストアです
サンドイッチを含む変更可能な オブジェクトであることに注意します
単集合インスタンスも テスト用に用意しています 目的を変更する時には SwiftUIへの指示が必要です
ObservableObjectプロトコルに準拠します
次に@Publishedで確認したいプロパティに マークを付けます
新しいモデルをどのように使用するのでしょう?
@Stateを使用し 値に関する 真実のソースを作成したのと同様に
@StateObjectを使用し 変更可能な オブジェクトの真実のソースを作成します
StateObjectはビューの変更時に自動的に オブジェクトを監視し ビューを更新します StateObjectを ビューコードに追加することも可能です これはApp全体のストアなので Appコードに入れた方がよいです
Appコードでどのように モデルにリンクできるかを確認します
最初に使用したコードです 先ほどのビューコードと 似ていることに注目してください Appプロトコルに準拠する 構造体です bodyプロパティではビューと同様に 必要なものを構築します WindowGroupを使用して Appのすべての ウィンドウで使用するビューを指定します このAppの特徴は @main属性を持っていることです Swiftにこの構造体がAppの 出発点であることを伝えます
ストアとStateObjectを追加します
AppはState StateObjectと その他の特殊なプロパティが使用できます 次にストアをビューコードに渡しましょう それをビューの初期化に渡します
ビューコードに戻ります
定数サンドイッチを ストアのプロパティに置き換えます
このオブジェクトを ObservedObjectにすることで 変更の監視をSwiftUIに通知します
ストアからサンドイッチを引き出すため Listを更新します
最後に プレビューを更新し テストストアを使用します
ストアからデータを引き出し 編集サポートを追加する準備ができました
スニペットからストアに変更を加えるための 便利な機能を紹介します
新しいサンドイッチの追加や サンドイッチの移動 それに削除が行えます
ForEachのListでは onMoveモディファイアを追加し
“moveSandwiches”メソッドを 呼び出せます
onDeleteを追加すれば
“deleteSandwiches”も
この変更を加えただけで Appに戻れます
スワイプしてListから 行を削除することもできます 削除するとSwiftUIから コールバックが呼び出され
ストアからサンドイッチが取り除かれます
UIが更新されて変更が反映されます Mac OSの編集サポートに 必要なものは以上です iOSでは編集モードに切り替える方法を 追加する必要があります 編集ボタンをツールバー項目に追加しましょう
toolbarモディファイアを使用して
SwiftUIビューを ツールバー項目に加えます その中に編集モードを自動で切り替えられる 編集ボタンを追加します iOSでのみ表示したいので ボタン周辺に“if os(iOS)”を追加し
ツールバーにのみ追加されるようにします
Listの編集モードを切り替えましょう
各データ行には編集コントロールがありますが 下部の静的要素にはありません SwiftUIは編集コントロールを 必要な行だけに自動的に表示します
アイテムの並べ替えや タップによる削除ができます
新たにサンドイッチを追加する ボタンを作るには toolbarモディファイアに別のビューを追加します このビューでは “add”というLabelが付いたボタンと
makeSandwichメソッドを呼び出す アクションを作ります
ボタンをタップすると 新しいサンドイッチが現れました
追加内容をざっと確認しましょう まず編集操作をListに追加する方法です
これらのモディファイアと
データを変更する関数のみ使用しました
サンドイッチタイプを 特定する方法を覚えていますか? ForEachはコレクションへの変更を監視し 正しい挿入と削除を合成するため Listに行を加除する指示は不要です つまりデータソースの 不整合の例外がなくなりました
toolbarモディファイアを使用し
Listを編集して新項目を加える ツールバー項目も追加しました 最小限のビューコードで この洗練されたList UIを作成できます
Appはすぐに構築できましたが 製品として必要な作業は まだあるとお考えでしょう Dynamic Typeやダークモード ローカリゼーションなどへの対応です SwiftUIを使えば それらも自動的に行えます プレビューを使用して簡単にテストできます プレビューを見てみましょう “プレビュープラス”ボタンをクリックし 2つ目のプレビューを追加します
次に“検査”ボタンをクリックして 新しいプレビューを設定
Dynamic Typeのサイズを より大きな値にします
見栄えが良くなりましたね
このプレビューの変更で 追加されたコードを見てみます
Xcodeはプレビューの環境で 値を設定するモディファイアを追加しました プレビュー環境では ビューに関するコンテキスト情報を設定でき 情報はビューの階層を流れ すべてのビューの側面を変更します ビューにカスケード変更を加えるのに便利です
別のプレビューインスタンスを追加しましょう
今回はプレビューインスペクタで
カラースキームを“暗い”に設定
すべてが自動的に表示されます 最後に 他言語による Appの動作を見てみましょう 英語の文字列ファイルを Appにドロップします
これらのファイルのローカライズを Xcodeに指示します
次にプロジェクトファイルに移動し
ローカリゼーションを アラビア語にインポートします
ビューコードに戻り 再度プレビューを追加します
環境のレイアウト方向を
右から左に設定します
既にすべてが機能していますね
最後にロカールの設定を
アラビア語に
ローカライズされました 私たちのコードが優れているのは
これらの機能への対応が簡単な点です ローカライズを可能にする 文字列のマークアップも不要でした 文字列リテラルを使用して テキストを自動的に推測しローカライズします
テキストはモデル値と同様に そのまま使用する必要があります 文字列補間を使用したローカライズも可能です ぜひSwiftUIをご活用ください これらの動作は無料で入手でき より迅速に 優れたAppを構築できます
構築した際は今一度見直し 正しく機能するかを確認しましょう ダークモードバージョンを使用し Appをライブにします 今回はデバイス上で実行します
iPhoneが接続されているので このボタンをクリックして
ビューを送信しプレビューします
サンドイッチのListです タップすると詳細が表示されます
タップでズームすると transitionのある “スパイシーな”バナーが非表示になります このアニメーションは常にインタラクティブです
Listの編集もできます
上に移動します
場違いなホットドッグは削除して
新しいサンドイッチを追加します
今回のAppについて 最後に1つ補足しておきます App全体を構築し 高性能な動作のテストを Appを一切使わずに実施した点です Xcodeプレビューにより Appの表示 編集 デバッグが未だかつてなく高速に行えます ご視聴 感謝します SwiftUIをお楽しみください
-
-
17:18 - Views are lightweight
struct SandwichDetail: View { let sandwich: Sandwich var body: some View { Image(sandwich.imageName) .resizable() .aspectRatio(contentMode: .fit) } }
-
18:30 - Views are composed
struct SandwichDetail: View { let sandwich: Sandwich var body: some View { Image(sandwich.imageName) .resizable() .aspectRatio(contentMode: .fit) } }
-
19:52 - View are dynamic
struct SandwichDetail: View { let sandwich: Sandwich @State private var zoomed = false var body: some View { Image(sandwich.imageName) .resizable() .aspectRatio(contentMode: zoomed ? .fill : .fit) .onTapGesture { zoomed.toggle() } } }
-
21:40 - Where is truth?
struct SandwichDetail: View { let sandwich: Sandwich @State private var zoomed = false var body: some View { Image(sandwich.imageName) .resizable() .aspectRatio(contentMode: zoomed ? .fill : .fit) .onTapGesture { zoomed.toggle() } } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。