以下のページを DeepL で 日本語翻訳したものです。
こんにちは、そしてWWDCへようこそ。
皆さん、こんにちは。私はJacob、後からKyleが参加します。より良いアプリをより速く構築するための素晴らしい方法であるSwiftUIをお見せできることをとても楽しみにしています。SwiftUIについて学ぶ最良の方法は、アプリを作って実際に動作を見ることだと考えています。SwiftUIを使うことは魔法のように感じますが、何も持っていないことを明確にするために、ゼロから始めてSwiftUIでアプリを作るプロセス全体を見ていきたいと思います。さて、どんなアプリを作ろうとしているのでしょうか?舞台を設定してみましょう。カイルと私はサンドイッチを食べるのが大好きです。だから、私たちは見つけられる最高のサンドイッチのリストをつけています。そのためのアプリを作りたいんです Xcodeに飛び込んで、作り始めましょう。まずは新しいプロジェクトを作るところから始めます。
マルチプラットフォームアプリのテンプレートを使います
"サンドイッチ "と名付けます これが新しいプロジェクトです Xcodeは、iOSとmacOSバージョンのアプリに必要なものをすべて用意してくれています。iOS固有のアセットのためのグループとmacOS固有のアセットのためのグループがあります。
しかし、コードのほとんどは共有グループにあります。ここにアプリのコードがすべて入っているファイルがあります。これはアプリ全体を設定するコードとしては短すぎると思われるかもしれませんが、必要なのはこれだけです。このコードについては、もう少し後に説明します。今のところ、アプリのビューのコードから始めましょう。
エディタの左側にはコードが表示され、右側にはそのコードを視覚的に表現したキャンバスが表示されます。SwiftUIでは、ビューの定義は単なるSwiftコードであり、キャンバスとコードエディタは同じコードを表示したり編集したりするための異なる方法にすぎないことを意味します。キャンバスで何かを選択すると、その選択はコードにも反映されます。そして、コード内の何かを変更した場合...
その変化はキャンバスにも反映されます。これらはシームレスに連携しているので、いつでも好きなときに移動することができます。では、その仕組みをもう少し詳しく説明しましょう。キャンバスは、ビューコードのプレビューを表示し、そのコードの編集や学習にも役立ちます。Xcode は、実際のコードをコンパイルし、結果を生成するために実行することで、これらのプレビューを表示します。しかし、私がプレビューについて気に入っていることの一つは、プレビューがSwiftUIのコードを使って作成されていることです。後ほど、プレビューをカスタマイズするために、このコードがどのように多くの力を与えてくれるのかを見てみましょう。さて、このアプリではサンドイッチのリストを表示します。リストのセルを作りましょう。この下にテキストを追加して、各サンドイッチの情報を表示します。ライブラリから追加します...
キャンバスの上にドラッグするだけで、それが表示されます。
そして、Xcodeはそれを様々な場所にドラッグしたときに何が起こるかを表示してくれます。ドロップすると、プレビューが更新され、追加されたテキストが表示されます。Xcodeは、これらのビューをVStackに埋め込んで、私が望むレイアウトにしてくれました。VStack、または垂直スタックは、SwiftUIで一般的なレイアウトコンテナの一つです。これにより、ビューを垂直にスタックすることができます。また、水平方向にビューをスタックできるHStackもあります。
そして、これらのスタックはコンテナです。その中に好きなビューを配置することができます。
このプレースホルダーをサンドイッチの材料に置き換えてみます。とりあえず、ハードコードされた値を使ってみましょう。
次に、テキストの隣に画像を追加します。エディタと同じようにコード内で簡単に編集できるので、ここのHStackにビューを埋め込んでみましょう。コマンドでビューをクリックして、"Embed in HStack "を選択します。そして、Xcodeはそのためのコードを追加してくれました。これで、VStackの横に画像を追加することができます。
アセットを少しずつ追加していきますが、今のところはSFのシンボル画像を使っていきます。
そして、すでにセルの基本的なバージョンが完成しています。では、セルのスタイルを変更してみましょう。ビューをコマンドでクリックして検査すると、そのビューのプロパティを見ることができます。
VStackの配置を変更してみましょう。
Xcodeはその値を表示するようにコードを更新してくれました。では、食材のテキストを検査してみましょう。今回はキャンバスを使ってみましょう。
プロのヒント:control-option-clickで直接インスペクタに移動できます。
小さめのフォントに変更してみます。Subheadlineを使ってみましょう。
Xcodeで自分のコードを編集していて良かったことの一つは、SwiftUIの使い方を学べることです。いくつかのテキストにフォントを設定するコードが見えてきました。これらの種類のメソッドを「モディファイア」と呼び、ビューの見た目や動作をカスタマイズするためにSwiftUIで使われています。フォアグラウンドカラーをセカンダリカラーに設定するためのモディファイアをコードに追加します。
セルができたので、リストに入れてみましょう。そのためには、セルをコマンドでクリックして「リストに埋め込む」を選択します。これでセルがリストに包まれ、そのセルを5回繰り返します。リストを表示するために必要なのは、このコードだけです。デリゲートもデータソースもなく、リスト内のビューだけです。次に、これをデータに接続してみましょう。先ほど作成したアセットとモデルファイルをドラッグインします。
私のモデルには、使用する情報のフィールドがいくつかあります。SwiftUIのリストで使用するためには、このタイプをIdentifiableにする必要があります。
これにより、リストに新しいアイテムがいつ入ってくるかを知ることができます。すでに'id'プロパティがありますが、これが必要なすべてです。
このモデルファイルには、アプリのデバッグに使用できるテストデータも含まれています。
では、ビューに戻ってデータを渡しましょう。
ビューにサンドイッチ用のプロパティを追加します。
プレビューの優れた点の1つは、独自のテストデータを使用できることです。ここにテストデータを入力します。
このバナーがプレビューの上に表示されているのに気づくかもしれません。サンドウィッチのプロパティを追加するなど、自分の型に大きな変更を加えると、Xcodeは更新を再開する準備ができるまでプレビューを一時停止します。このボタンをクリックするか、command-option-Pを押すことで、更新を再開することができます。
次に、リストを駆動するためにデータを使用してみましょう。
サンドイッチをリストに渡します...
サンドイッチの名前を表示するように テキストを更新します
そして、正しい材料の数を表示します。
実際の画像ができたので、サンドイッチのサムネイルを画像に使ってみましょう。
セルが微妙に変わっていることに気がついたかもしれません。私たちが始めたときは、標準の44ポイントの高さでした。しかし、これらの大きな画像に変更すると、セルは自動的に拡大され、余分な作業をすることなく画像が収まるようになりました。そして今では、これらの画像が文脈の中にあるので、少しシャープに見えるようになりました。別の修飾子を使って、画像に角の半径を適用してみましょう。
どのようなモディファイアが利用できるかわからない場合は、Xcodeライブラリのここにあるモディファイアのリストを表示したり、フィルタリングしたりすることができます。
コーナーラジアス修飾子を見つけたら、それをキャンバスにドラッグします。
リストセル内にあるビューについては、Xcodeはこれらのセルが同じ定義を共有していることを認識しているので、この修飾子はすべてのセルに適用されます。
この修飾子を画像の上にドロップして、値を微調整してみましょう。
これで、セルとリストが良い感じになったので、次に必要なのは、セルをタップしてサンドイッチの詳細を見ることができるようにすることです。
これをサポートするために、リストをNavigationViewで包んでみましょう。
NavigationViewを使うと、アプリの様々な部分を移動することができます。iPhoneでは、ナビゲーションバーが表示され、ナビゲーションスタックにプッシュすることができます。ビューのナビゲーションタイトルをバーに「Sandwiches」と表示するように設定してみましょう。
そして、スタックにプッシュするようにセルを設定します。そのためには、セルの内容をNavigationLinkでラップします。
NavigationLinkは、プッシュする先を指定します。今のところは、サンドイッチの名前を示すテキストを使って、NavigationLinkの内容としてセルを配置しています。
そして、UIが自動的に更新されて、すべてのセルにこれらの詳細指標が表示されるようになったのがわかると思います。
SwiftUIはこのような詳細を自動的に処理してくれるので、UIがデフォルトで正しく見えるようになっています。セルが正しく動作することも確認してみましょう。そして、ここでもプレビューは素晴らしいです。プレビューの再生ボタンをクリックすると、ライブ画面が表示されます。これにより、キャンバス内で実際のコードを操作することができます。
セルをタップして、期待通りにプッシュしたりポップしたりすることができます。
そして、スワイプしてポップすると、SwiftUIが自動的に与えてくれた高度な動作に気づくでしょう。セルはハイライトされたままで、スワイプするとインタラクティブにハイライトが解除されます。
このリストの最後の変更点は、リスト内のサンドイッチの数を表示することです。しかし、ビューのコードが少し大きくなってしまいました。そして、一つの巨大なビューで終わらせたくありません。そこで、いくつかの懸念事項を分離するために、セルをそれ自身のビューになるように因数分解してみましょう。Xcodeはこれを簡単な操作で実現してくれます。必要なビューをコマンドでクリックして
そして、"サブビューの抽出 "を選択します。すべてのビューコードはこの新しいビューに移動され、その名前を選択することもできます。"SandwichCell "と呼ぼう サンドイッチのプロパティを追加して...
サンドイッチのプロパティを追加して...
これは素晴らしいワークフローの改善です。また、SwiftUIでは、ビューは非常に軽量なので、ロジックをより良くカプセル化したり分離したりするために余分なビューを作成することを心配する必要はありません。
リストコードがスリム化されたので、サンドイッチの数の行を追加してみましょう。
今のところ、リスト全体を駆動するために単一のコレクションを使用しています。
しかし、もっと多くのものが必要な場合、SwiftUIはリストや他のコンテナに静的なコンテンツと動的なコンテンツを混在させることもできます。このコレクションをリストに渡すことをForEachで置き換えることができ、コレクション内の各アイテムのビューを作成します。
これで、このデータ駆動型の要素のすぐ横に静的な要素を追加することができるようになりました。このForEachの下にテキストを追加して、サンドイッチの数を表示させます。
そして、前景色の色を二次的な色に変更します。
また、テキストを中央に表示します。そのためには、テキストをHStackに埋め込んで...
に埋め込み、スペーサーを追加します。
スペーサーはSwiftUIでは一般的なレイアウト要素です。ツールバーの柔軟なスペースのように振る舞い、利用可能なスペースを埋めるために拡張します。そのため、この2つのスペーサーは利用可能なスペースを分割し、テキストを中央に配置します。
次に、詳細ビューを作ってみましょう。SwiftUIのビューテンプレートを使って新しいビューを作成します...
名前は "SandwichDetail "です。Xcodeは自動的にビュー構造体とそれを作成するためのプレビューコードを与えてくれました。
この詳細ビューでサンドイッチの詳細情報を表示したいので、入力としてそれを渡します。
先ほどと同じように、プレビューコードを使用して、テストデータを使用したバージョンのビューをセットアップします。
ビューを構築するために、サンドウィッチの画像名の画像を使用します。
画像が表示されていますが、この画像はビューには大きすぎます。デフォルトでは、SwiftUIはすべての画像をコンテンツのサイズで表示し、画像の拡大縮小による視覚的なアーチファクトを防ぎます。しかし、このような写真の場合、画像のサイズを縮小できるようにしたいと思います。そこで、画像固有のサイズ変更可能修飾子を使って指定することができます。
これは画面のサイズですが、画像の元のアスペクト比を維持したいのです。これには別の修飾子を使ってアスペクト比を設定することができます。
これで、画像を拡大してフレーム全体を占めるようにする「塗りつぶし」か、フレーム内に画像が収まるようにする「フィット」のどちらかを選択できます。プレビュー機能を使えば、これらの違いを簡単に見て理解することができます。とりあえず「フィット」を使ってみましょう。
さて、リストに戻り、セルをタップすると新しい詳細ビューが表示されるようにセルを更新してみましょう。
SandwichDetail...を作成して、現在のサンドイッチを入力します。
を作成し、現在のサンドイッチを入力します。
プレビューをライブモードに戻します。これでセルをタップして画像を見ることができるようになりました。
しかし、ここでプレビューしているので、ナビゲーションバーにタイトルを設定するのを忘れていたことがわかります。詳細表示に戻って修正してみましょう。
ここに同じナビゲーションタイトルを追加して、タイトルをサンドイッチの名前に設定します。
しかし、ここにあるプレビューでは、ビュー自体を見ているだけなので、自分の変更をすぐに確認できるようにしたいです。プレビューにはSwiftUIのビューのすべての機能があるので、それが可能です。SwiftUIのコードの他の場所と同じように、プレビューをNavigationViewに設定することができます。
ビューのプレビューにはナビゲーションバーがあり、そこに自分のタイトルを見ることができます。さて、美味しいサンドイッチを選ぶとき、私にとってとても重要なことが一つあります。サンドイッチにはソースの量が必要です。ソースがないとパサパサになる 多すぎるとソースに溺れてしまう サンドイッチにソースがあるのは分かるが多すぎないことを確認したい もしこれがアスペクト比が "充填 "だったら... サンドイッチを間近で見ることができました。美味しそうですね。私が本当に欲しいのは、サンドイッチを間近で見るための「フィル」と、サンドイッチ全体を見るための「フィット」を行ったり来たりできるようにすることです。しかし、アプリの実行中にこのアスペクト比のコンテンツモードを動的に変更するにはどうすればいいのでしょうか?これを行う方法を理解するためには、ビューがSwiftUIでどのように動作するのか、その理由についてもっと知る必要があります。それについてはKyleに話を譲ることにします。ありがとう、Jacob。こんにちは。SwiftUIチームのKyleです。SwiftUIはあなたが慣れ親しんでいるものとは少し違うかもしれませんので、先に進む前に一歩下がって、ビューの働き方について話しましょう。先ほど、SandwichDetailビューの実装を終えました。
SwiftUIでは、ビューはUIViewのような基底クラスを継承するクラスではなく、ビュープロトコルに準拠した構造体であることに注意してください。これは、ビューが保存されたプロパティを継承しないことを意味します。スタック上に確保され、値で渡されます。
SandwichDetailはサンドイッチを格納するだけなので、サンドイッチのサイズと重さであり、追加の割り当てや参照カウントはありません。
裏では、SwiftUIは積極的にビュー階層をレンダリングのための効率的なデータ構造にコラプスしています。このため、SwiftUIでは、小さな単一目的のビューを自由に利用しています。あなたもそうすべきです。
ここで取り上げてほしいのは、SwiftUIではビューは信じられないほど軽量であるということです。Jacobが先に述べたように、サブビューを抽出することで実行時のオーバーヘッドがほとんどないので、SwiftUIのコードをリファクタリングすることを決して躊躇してはいけません。SwiftUIのビューと従来のUIフレームワークのビューは同じ主要な役割を果たします。ビュープロトコルが必要とするのは、bodyという単一のプロパティだけです。これはそれ自体がビューです。
より小さなビューを組み合わせることで、より大きなビューを構築することができます。私たちは、Image(ネイティブ解像度での画像のビュー)を組み合わせてSandwichDetailビューを構築しました。
リサイズ可能、画像をいずれかの次元に引き伸ばすビュー...
と、その子を比例的にスケーリングするビューである aspectRatio を組み合わせて、SandwichDetail ビューを構築しました。
SandwichDetail のように作成したビューのレンダリングは、その本体のレンダリングだけです。
body の実装にブレークポイントを設定して、デバッガがそこで止まった場合、フレームワークがビューの新しいレンダリングが必要だと判断したことを意味します。
ジャーン! フレームワークは新しいレンダリングを取得するタイミングを知っています。なぜなら、UI の一部を定義するだけでなく、ビューはその依存関係を定義するからです。
SandwichDetailを拡張して、ユーザーがタップして、空いたスペースに収まるか収まらないかを切り替えられるようにしてみましょう。
最初に必要なのは、画像がズームされているかどうかを示す状態変数です。
SwiftUIは状態変数を持つビューを見ると、ビューに代わってその変数のための永続的なストレージを割り当てます。
その状態変数に基づいて塗りつぶしやフィットを決定すると、ズームされているときはこのように、ズームされていないときはこのようにレンダリングされるビューができます。
あとは、タップジェスチャーで 2 つの状態の間を行ったり来たりするだけです。タップすると、画像はズームして塗りつぶされ、縮小してフィットします。
では、実際にタップすると何が起こるのでしょうか?状態変数の特別な特性の1つは、SwiftUIが読み書きされたときに観測できるということです。SwiftUIはここでズームされたものがボディで読み込まれたことを知っているので、ビューのレンダリングがそれに依存していることを知っています。つまり
変数が変更されると、フレームワークはボディを再度取得し、新しい状態値を使用してレンダリングを更新し、今度は異なるコンテンツモードでレンダリングを更新することができます。従来のUIフレームワークでは、ステート変数と古いプロパティを区別していません。しかし、私はこの区別が信じられないほど明確であることに気づきました。SwiftUIでは、スクロールビューのオフセット、ボタンのハイライト、ナビゲーションスタックの内容など、UIがそれ自身を見つける可能性のあるあらゆる状態は、しばしば「真実のソース」と呼ばれる権威あるデータから導き出されます。状態変数とモデルをまとめて、アプリ全体の真理の源を構成します。
先ほど、この aspectRatio の呼び出しがビューを作ることを説明しました。
その定義は次のようなもので、contentModeは古いSwiftのプロパティです。
すべてのプロパティを、真実のソースまたは派生値のいずれかとしてきちんと分類することができます。
ズームされた状態変数は真実のソースです。
contentModeプロパティはそれから派生したものです。
思い起こせば、SwiftUIは状態変数が読み書きされたときに観測することができます。そのため、1つが変更されたとき、どのレンダリングをリフレッシュするかを知っています。
フレームワークは、新しいボディを要求して新しい aspectRatio ビューをゼロから作成することでレンダリングをリフレッシュし、それによって contentMode とその他の保存されたプロパティをオーバーライドします。
これが、SwiftUIですべての派生値が最新の状態に保たれるメカニズムです。
すべての状態変数が読み書き可能な真理のソースであることを見てきました...
そして、すべての古いプロパティは読み込み専用の派生値であることを見てきました。この話では例を見るつもりはありませんが、SwiftUIは読み書きの派生値を渡すための "binding "と呼ばれるツールを発明しました。そして、技術的には、どんな定数でも読み取り専用の真実の完全なソースとして機能します。プレビューを動かしているテストデータはその例です。
最後に、状態変数とモデルがアプリ全体の真理のソースを構成していることを先に述べました。後ほど、Jacobが観測可能なオブジェクトを使ってモデルオブジェクトへの変更を観測する方法をSwiftUIに教えるのを見てみましょう。
これらのプリミティブの違いがまだはっきりしていなくても心配しないでください。
これらのデータフロープリミティブのうち、いつどのデータフロープリミティブを使うかについての勘を養うためのセッションがあります。いいですね。ここでは一歩下がって 棚卸しをしてみましょう ここまで見てきたことは、従来のUIフレームワークで行うこととは大きく異なります。
従来のUIフレームワークを使用しているときは、このような観点では考えないかもしれませんが、ビューがデータを読み込むたびに、暗黙の依存関係が発生します。なぜなら、データが変更されると、ビューは新しい値を反映するために更新する必要があるからです。
それが失敗した場合、それはバグです。SwiftUIは自動的にあなたに代わって依存関係を管理し、適切な派生値を再計算することで、このようなことが二度と起こらないようにしてくれます。もちろん、一度に一つの依存関係を管理するだけではありません。私たちが取り組んでいるUIは大きくて複雑です。どれだけの量を頭に入れておかなければならないか、どれだけ簡単に間違いを犯すかということになると、今日の手動での依存関係の管理方法は本当に大変です。私の最善の努力にもかかわらず、これまでに出荷したすべてのアプリのアップデートにはUIのバグがありました。その一行一行が依存関係です。
そして、それらをすべて理解した後でも、イベント ハンドラ コールバックのすべての可能な順序で UI が一貫した状態になっていることを確認する必要があります。
その意味を明確にするために、UIKitで実装された古いバージョンのSandwichesアプリのバグを見てみましょう。これはビューコントローラのコードのスケッチです。拡大したときには、おしゃれなエンハンスボタンが付いていました。
このように、ジェイコブがこの画像のような低解像度の画像になっても、サンドイッチにソースが入っていることを確認することができます。
ボタンをタップすると バックグラウンドのスレッドに 機械学習を実行して 画像を強調するんだ
ああ、その方がいい。茶色のマスタードをスパイシーにしたような気がする 一つだけ問題があった 迷走中の活動表示が回転を止めないという報告がありました。今回のバグは、このような予想外の順番で発生したものでした。
この種のミスは、真実のソースを更新してそこからUIを派生させるのではなく、イベントハンドラのコールバックで直接サブビューを変異させてしまうと簡単にできてしまいます。
これは、すぐに頭に浮かんでくる幸せなパスにコード化せずにはいられず、そうでない不幸なパスを見落としてしまうからです。
問題は、イベントの数が増えると、不幸なパスの数が爆発的に増えてしまうことです。仮に4つのイベントをすべて得たとしましょう。どのように多くの異なる順序が考えられるでしょうか?実際には、4つのイベントハンドラが呼ばれる可能性のある24の異なる順序があります。実際には、これらのイベントのそれぞれが複数回発生する可能性があるため、これよりもさらに悪いことになります。たとえば、ユーザーが強化ボタンを押したとします。この複雑さを管理することの難しさは、非同期コールバックをジャグリングしたり、割り込み可能なアニメーションを実装したりしたことがある人なら誰でも知っているはずです。これらの補完ハンドラは、あらゆる種類の予期せぬタイミングで起動する可能性があります。
5年前の自分に、自分の仕事について一つ言えるとすれば、UIプログラミングは難しいということだ。マルチスレッド コードの同期化が簡単だとは誰も思っていません。私が書いたいくつかのマルチスレッドコードのバグを取り除くのに何ヶ月もかかりました。それでも、その正しさに100%の自信を持つことはできませんでした。UI コードの多くは、実際にはそのようなものです。多くの場合、ビューが欠けていたり、間違った場所にあったりするだけなので、私たちはその難しさを軽視していると思います。しかし、そうするべきではありません。レース条件と UI の不整合は、見落としがちな順序付けという複雑さの根底にある共通点を持っています。私たちが作業しているビューの多くは、4つ以上のイベントを処理しなければなりません。モデル通知、ターゲットアクション、デリゲートメソッド、ライフサイクルチェックポイント、完了ハンドラ、これらはすべてイベントです。12個のビューは、およそ12の階乗の可能な順序付けに相当します。これはほぼ5億個です。これは、あなたの脳のBig O表記法のようなものだと考えることができます。
あなたは人間です 一度に頭の中に入れることができる量は限られている この点線は?あなたのアプリです この点の違いは何だと思う?その通り バグです
機能を追加すると、可能な順序の数が爆発的に増え、1つを見落とす可能性が高くなり、バグは避けられません。
伝統的なUIフレームワークを使用しているときに、ビューの更新をすべて1つのメソッドに集めることで、そのシンプルさに気付いた人も多いと思います。これを行うと、先ほど見たような曲線の後ろ側を崩すことになります。なぜなら、メソッドが1つしかない場合、呼び出すことができる順序は1つしかないからです。
このように考えたことがないかもしれませんが、このパターンでは、UIがそれ自身を見つける可能性のあるすべての状態に対して、真実のソースを定義し、その真実のソースの集合体からビューのプロパティを導出することを強制します。
もしこれが聞き覚えがあるように聞こえるなら、SwiftUIはこのベストプラクティスに直接触発されたからです。私たちは、"body "が呼び出される唯一のエントリーポイントにすることで、フレームワークの中でそれを成文化しました。
そうすることで、従来のUIフレームワークを使用していたときに、少なくとも私がこのパターンに当てはまることができなかったようなトリッキーなケースを解決しました。例えば、サブビューの削除、ナビゲーションスタックへのプッシュ、テーブルビューの更新などです。これがViewsだけでなく、AppsやScenes、その他のボディを持つSwiftUIの抽象化がそのように動作する理由です。なぜなら、あなたはただの人間であり、変更されたUIの部分の新しいインスタンスを単純に取得するというこのパターンは、あなたの脳と一緒にスケールし、実質的にUIの矛盾を排除しています。さて、デモに戻って SandwichDetail ビューを完成させましょう。ジェイコブ? ありがとう、カイル。サンドイッチをもっと近くで見られるように "zoomed "というプロパティを追加しましょう...
"zoomed "というプロパティを追加してデフォルトは "false "にしましょう 状態はビューの実装内でしかアクセスできないので、プライベートにしましょう。
そして、これをアスペクト比のコンテンツモードで使用して、ズームしているときは "fill "で、そうでないときは "fit "に変更します。
そして最後に、ズーム状態を切り替えるタップジェスチャーを追加します。
ライブプレビューで試してみましょう。
これで、これらのモードを切り替えることができるようになりました。
しかし、ズームしているとき、下の方に余白があることに気づくかもしれません。SwiftUIは自動的に安全領域と呼ばれる場所にビューを配置します。これは、アプリのUI要素がコーナー半径のようなものによってクリップされることがないことを意味します。
しかし、このような端から端までの画像の場合、実際には画面全体に拡大したいと思います。
そのためには、安全領域を無視する修飾子を追加します。
具体的には下の端を無視します。
近づいてきましたが、何かが足りません。
これにはアニメーションが必要で、SwiftUIではアニメーションを追加するのはとても簡単です。アニメーションを追加するのはとても簡単です。
と、その異なる状態の間でアニメーションするようになりました。
そしてそれだけでなく、アニメーションは完全にインタラクティブで割り込み可能です。いつでもタップすることができ、常に正しくアニメーションします。
次に強化ボタンを追加したいと思っていましたが、カイルがモデルを訓練した方法では、彼が示した1つの画像でしか機能しないことがわかりました。だから、もっと便利なものを追加しようと思います。カイルはこのようなスパイシーなサンドイッチが好きです。
しかし、私はそうではないので、サンドイッチが辛いかどうかを素早く知る方法が欲しいのです。そのためのインジケータを詳細ビューの下に表示してみましょう。
既存のサンドイッチ画像の周りにVStackを追加します。
そして、そのVStackに適用する一般的な修飾子を移動させます。
ここでは画像とテキストを表示したいのですが、そのための素晴らしい方法がラベルです。
ラベルはタイトルを表示するためのもので、ここでは "Spicy "を使用し、関連するアイコンも表示します。システムイメージを使用します...
"flame.fill "というシステムイメージを使う このラベルはアイコンとタイトルを一緒に表示してくれる 他の文脈でも使えます リストやメニューのように 自動的に正しい外観、間隔、大きさになります
私が想像していたのは、画面の一番下にあるバナーの外観で、その後ろに背景があるようなものです。バナーを下に移動させるには、スペーサーを追加します...
これでバナーは下に、画像は上に移動します。
そして、画像の中央を保つために、画像の上にもう一つスペーサーを追加します。
スペーサは、要素間のパディングを維持するために、自動的に最小サイズが設定されています。しかし、この場合、画像はコンテナの端まで届くようにしたいのです。そこで、最小長さをゼロに設定してみましょう。
また、バナーが表示されていても画面の端にぶつからないように、バナーにパディングを追加してみましょう。
インスペクタで... このボタンをクリックして、このビューのパディングをオンにすることができます。
これでいいでしょう。そして、フォントサイズも上げてみましょう。
テキストサイズが大きくなっただけでなく、シンボル画像も大きくなったことに注目してください。シンボル画像は、テキストと同じフォント情報を自動的に使用して適切なサイズになるようにしています。見出しフォントを使ってみましょう。
さて、この画面をより辛くするために、背景を赤にしてみましょう。
背景修飾を使うと、適用されたビューの後ろに任意のビューを配置することができます。これは一般的にソリッドカラーと一緒に使用され、ビューの背景をソリッドカラーにします。
さて、私たちのビューの後ろには赤が表示されています。しかし、なぜこの小さな領域だけなのでしょうか?SwiftUIでは、ビューはコンテンツに合わせてサイズを調整します。この場合、画像とテキストは自然なサイズで、パディングを適用するためのスペースもあります。先ほどと同じように、スペーサーとHStackを追加することで、端から端まで拡大することができます。
さて、最後の仕上げとして、前景色の色を黄色にして、スパイシーなテーマに合わせてみましょう。
そして、フォントを小さなキャップを使用するように更新します。
いい感じです。バナーはできましたが、サンドイッチがスパイシーな時だけ表示させたいですね。どうすればいいのでしょうか?宣言構文を使えば簡単にできます。if "を使えばいいのです。サンドイッチが辛いかどうかをチェックして...
辛ければバナーを表示します
これを確認するために、プレビューデータを変更して、辛くない別のサンドイッチを表示させることができます。しかし、さらに良いことに、プレビューを設定して、複数のバージョンのビューを表示させることができます。プラスボタンをクリックして、このプレビューの別のコピーを追加することができるんです。
そして、使用しているデータを更新して...
を更新して、異なるサンドイッチを表示するようにします。これで、スパイシーなバナーが付いたバージョンと付いていないバージョンが表示されるようになりました。
このようにして、私たちが編集をするときに、両方のバージョンのビューが私たちの望むように動作することを確認することができます。
そして、Xcodeが別のプレビューを追加した方法は、ビューの別のインスタンスを追加することであることに注目してください。
このバナーは気に入っているのですが、ズームインしているときにサンドイッチの画像からスペースを奪ってしまうのは嫌です。そこで、ズームしたときの画像を非表示にしてみましょう。
これで、ズームするとバナーが表示されたり非表示になったりするようになりました。
そして、アニメーションもします...フェードインとフェードアウトします。
別のトランジションを設定することで、アニメーションの動作をカスタマイズすることもできます。
下端の「.move」を使ってみましょう。
これでスライドアウトして、スライドインします。
アニメーションが動いているときにタップしている間によく見ると...
向きを変えて戻ってくるのがわかります。何をしても、すべてがインタラクティブなままで、常に正しい場所に収まるようになっています。これが詳細ビューです。先ほど構築したものを確認してみましょう。
詳細ビューはサンドイッチを表示するように設定されています。これは、ビューの親から渡された派生値です。また、ズームしているかどうかのステートプロパティがあり、フレームワークによって保持され、アスペクト比のコンテンツモードを制御します。
また、バナーもあります。これは、スパイシーサンドウィッチの場合のみ表示され、ズームされていない場合にのみ表示されます。
スライドイン、スライドアウトさせるためのトランジションも指定しています。そして、その遷移の間に実際に何が起こっているのでしょうか?ビューが削除されると、ビューは画面外の新しい位置にアニメーションします...そしてSwiftUIはアニメーションが終わるまで待って、実際に階層からビューを削除します。ビューが戻ってくると、SwiftUIは画面外に挿入し、アニメーションで元の位置に戻します。アニメーションを使って簡単に階層からビューを追加したり削除したりできるのはとても素晴らしいことです。
そして、このアニメーションは箱から出してすぐに常にインタラクティブであることを思い出してください。
これこそが、イベント駆動ではなく、データ駆動であることの真価を発揮するところです。カイルが話していたイベントはすべてアニメーション中にも起こる可能性があります。アニメーションの開始と終了は、さらに多くのイベントです。イベント駆動の世界でこのようなものを構築するのは非常に難しいですが、SwiftUIではたった1行のコードで済みます。
さて、サンドイッチのリストに戻って、このアプリを完成させましょう。
始めた当初はマルチプラットフォーム対応のアプリテンプレートを使っていましたが、今のところiPhoneしか見ていません。これを他のプラットフォームで動かすにはどのくらいの作業が必要なのでしょうか?見てみましょう。ラン先をiPhoneからiPadに切り替えてみる...。
で、本番へ。
SwiftUIはナビゲーションビューをスプリットビューに変換してくれたので、左にサンドイッチを選んで...右に表示させることができます。
プレビューで気づいたことは、サンドイッチが選択されていないときは、空白の領域が表示されることです。これを改善して、サンドイッチを選択するというプレースホルダを表示できるようにしたいと思います。そのためには、ナビゲーションビューに2つ目のビューを追加するだけです。
スタックに複数のビューを追加できるのと同じように、ここにも複数のビューを追加することができます。
しかし、これらのビューはスタックされるのではなく、ナビゲーションビューに与えられ、最も適切な方法で表示されます。この場合、最初のビューは左側に表示され、2番目のビューは右側のビューのプレースホルダーになります。iPhoneの場合、プレースホルダは不要なので自動的に削除されます。
macOSではどうなるか見てみましょう。
ここでも非常にうまく機能していて、iPadと同じプレースホルダが表示されます。
すべてのAppleプラットフォーム間で同じビューコード、モデルコード、アプリコードを使用することができます。また、このプレースホルダのように、プラットフォームごとに改良を加えて、さらに進化させることもできます。
時間の経過とともに、サンドイッチのリストを変更できるようにする必要があります。そこで、編集サポートを追加しましょう。また、データモデルをもう少しリアルにしてみましょう。現在、アプリのデータは完全に静的です。サンドイッチの配列があります...
最初のものは常に何でもあります モデルを更新して、サンドイッチを格納するルートストアオブジェクトを持つようにして、時間の経過とともに変更できるようにしましょう。
サンドイッチを格納するためのモデルファイルをドラッグします。
このストアはデータストアであり、サンドイッチを販売する場所ではありません。
このストアは、サンドイッチを格納するミューターブルオブジェクトであることに注意してください。
また、テスト用のシングルトンインスタンスも用意しています。あとは、オブジェクトが変更されたときにSwiftUIに伝えるだけです。
そのために、ObservableObjectプロトコルに準拠させるつもりです。
そして、観察したいプロパティを@Publishedでマークすることができます。
では、新しいモデルはどうやって使うのでしょうか?以前に @State を使って値の真偽を調べたように...
StateObjectを使って、変更可能なオブジェクトの真実のソースを作るのと同じように、@StateObjectを使うことができます。
StateObjectは自動的にオブジェクトを観測して、オブジェクトが変化したときにビューを更新します。そして、そのStateObjectをここのビューコードに追加することができます。しかし、これはアプリ全体のストアなので、もっと良い場所があります。アプリ コードに戻ってもっと詳しく見て、モデルにリンクする方法を見てみましょう。
これが最初のコードです。先ほどのビュー コードと非常によく似ていることに注目してください。アプリ プロトコルに準拠した構造体があり、ボディ プロパティがあります。この場合、WindowGroupがあり、アプリ内のすべてのウィンドウに使用するビューを指定できます。このアプリの特別な点は、@main属性を持っていることです。これは、この構造体がアプリの出発点であることをSwiftに伝えています。
ここにストアとStateObjectを追加します。
アプリはビューのようにState、StateObject、その他の特別なプロパティを使用することができます。次に、ストアをビューコードに渡してみましょう。ビューのイニシャライザに渡します。
ビューのコードに戻ります。
定数のサンドイッチを... ストアのプロパティに置き換えます
そして、SwiftUIにこのオブジェクトをObservedObjectにすることで、変更を観察したいことを伝えます。
そして、ストアからサンドイッチを取り出すようにリストを更新します。
最後に、テストストアを使うためにプレビューを更新しましょう。
いいですね。これで、ストアからデータを取得できました。これで、編集サポートを追加する準備ができました。
スニペットからお店の変更をするための便利な機能をいくつか落とし込んでみます。
新しいサンドイッチを追加するためのものと
サンドイッチを移動させるためのものと、サンドイッチを削除するためのものがあります。
ForEach リストに onMove モディファイアを追加することができます。
これは "moveSandwiches" メソッドを呼び出すものです。
そして、"deleteSandwiches" メソッドを呼び出す onDelete...
を追加して "deleteSandwiches" を呼び出します この変更でアプリに戻れば...
スワイプでリストから行を削除できます スワイプして削除するときはいつでも、SwiftUIはコールバックを呼び出します...
これでストアからサンドイッチが削除されます。
そして、UIは自動的に変更を表示するように更新されます。macOSでは、これが編集サポートに必要なすべてです。しかしiOSでは、スワイプで削除する方法に加えて、明示的に編集モードに入る方法を追加する必要があります。そこで、ツールバーの項目として編集ボタンを追加してみましょう。
ツールバーのモディファイアを使えば
SwiftUIのビューをツールバーアイテムとして追加することができます。その中に編集ボタンを追加します。これは自動的に編集モードを切り替えるコントロールです。これは自動的に編集モードを切り替えるコントロールです。
を追加して、ツールバーにのみ追加されるようにします。
それでは、リストの編集モードを切り替えてみましょう。
すべてのデータ行には編集コントロールがあり、下部の静的要素にはないことに注意してください。SwiftUIは自動的に編集コントロールを必要とする行のみに表示し、必要のない行からは省略します。アイテムの並び替えもタップで削除もできる
新しいサンドイッチを追加するボタンも追加してみましょう。ツールバーの修飾子に別のビューを追加して、"Add "というラベルのボタンにします。
そして、makeSandwichメソッドを呼び出すアクションにします。
ボタンをタップすると、新しいサンドイッチができあがります。
今追加したものをすぐに確認してみましょう。リストに編集操作を追加する方法を見てきましたが...
これらの修飾子だけで...
そして、データを変更するための簡単な関数があります。
また、先ほどサンドイッチの型を識別できるようにしたことを覚えていますか?ForEachは自動的にコレクションへの変更を監視し、正しい挿入と削除を合成してくれるので、リストに行の追加と削除を指示する必要がありません。
また、リストを編集するためのツールバー項目を追加するためにツールバー修飾子を使用しました。
を使用して、リストを編集したり新しい項目を追加したりするためのツールバー項目を追加しました。これが私たちのリストです。本当に最小限のビュー コードだけで、この洗練されたリスト UI を作りました。
私たちはこのアプリを本当にすぐに構築することができました。しかし、このアプリを顧客に提供するためには、まだまだ多くの作業が必要だと思うかもしれません。最近では、Dynamic Type、Dark Mode、ローカリゼーションなどのサポートがアプリに期待されています。しかし、SwiftUIでは、これらの動作を自動的にサポートしています。プレビューを使って、これらのすべてを素早くテストすることができます。プレビューに行って見てみましょう。プレビューの「プラス」ボタンをクリックして、2つ目のプレビューを追加します。
そして、この「検査」ボタンをクリックして、新しいプレビューを設定します。ダイナミックタイプのサイズをもっと大きな値に設定します。
すると、すべてが自動的に素晴らしいものになります。このプレビューを変更するために追加されたコードを見てみましょう。
Xcodeはプレビューの環境に値を設定するモディファイアを追加しました。環境とは、ビューに関するコンテキスト情報を設定する方法で、ビュー階層を流れて、含まれているビューの側面を一度に変更することができます。これは、ビューとその子にカスケード的な変更を加えるのに最適です。
別のプレビューインスタンスを追加してみましょう。
今回は、プレビューインスペクタで...
配色を「ダーク」に設定してみましょう。再び、すべてが自動的に良く見えるようになります。最後に、他の言語でアプリがどのように動作するか見てみましょう。いくつかの英語の文字列ファイルがあります...それをアプリにドロップします。
Xcodeにこれらのファイルをローカライズするように指示します。
プロジェクトファイルに移動して...
アラビア語にローカライズをインポートします
さて、ビューコードに戻って、もう一つプレビューを追加してみましょう。
環境のレイアウト方向を設定すると...
を "rightToLeft" に設定すると...
これは素晴らしいことです。
最後に、ロケールをアラビア語にすると...
をアラビア語に設定すると...
アプリはローカライズされています。しかし、さらに良いことに、コードを振り返ってみると...
これらの機能をサポートするために 特別なことは何もしていません。テキストをローカライズできるようにするために、どの文字列をローカライズすべきかをマークアップする必要はありませんでした。SwiftUIは「サンドイッチ」のような文字列リテラルを持つテキストはデフォルトでローカライズされるべきだと自動的に推測します。
しかし、モデルの値のように文字列から作成されたテキストはそのまま使用されるべきです。また、文字列補間を使用して正しくローカライズすることもできます。SwiftUIを使ってアプリを作り始めるのは本当にワクワクします。これらのビヘイビアをすべて無料で手に入れることができれば、アプリのユニークな部分に集中して、顧客のためのより良いアプリをさらに早く構築することができます。
最後にアプリを通過して、構築したものを確認し、すべてが正しく動作していることを確認してみましょう。ダーク モード バージョンを使用して、アプリを再びライブで使用してみましょう。でも、今回はデバイスでやってみましょう。
iPhoneを接続しているので、このボタンをクリックしてみましょう。
をクリックして、デバイスにビューを送信してプレビューしてみましょう。
サンドイッチのリストが表示されているので、タップして詳細情報を見ることができます。
詳細表示では、タップしてフルスクリーンにズームすると、「スパイシー」バナーがトランジションで非表示になります。このアニメーションは常にインタラクティブです。
そして、リストを編集して変更を加えることができます。
これを上に移動させてみましょう。
私は純粋主義者だ ホットドッグはサンドウィッチには入らないと思います。
新しいサンドイッチを追加できる
これがアプリだ 最後に一つ指摘しておきたい事がありますこれは我々が見ていなかった事です 私たちはこのアプリケーション全体を構築し、一度もアプリを構築して実行することなく、これらのリッチな動作のすべてをテストしました。Xcodeのプレビューは、アプリケーションの表示、編集、デバッグを可能にしてくれます。