ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
SwiftUIの徹底解説
SwiftUIの哲学の核となる考え方を別の見方から覗いてみましょう。アイデンティティ、ライフタイム、および依存性。一般的なパターンを探し出し、フレームワークを動かす原理を把握し、それらを使ってAppの正しさとパフォーマンスを保証する方法を見出すことができます。
リソース
関連ビデオ
WWDC23
WWDC22
WWDC21
WWDC20
WWDC19
-
ダウンロード
♪ (SwiftUIの徹底解説) こんにちは Mattです 後でLucaとRajが加わります 今日は SwiftUIを徹底解説していきます さて SwiftUIが宣言型のUIフレームワークで あることは これまで何度か耳にしてきました つまり あなたがAppに求めるものを 高いレベルで説明すると SwiftUIはそれを実現する方法を 正確に決定してくれるのです ほとんどの場合 これは素晴らしいことです それはSwiftUIが魔法の ように感じられるときです しかし SwiftUIが思いもよらないことをする 瞬間は常にあります そんな時にはSwiftUIが 舞台裏で 何をしているのかをもう少し理解すると 求めている結果を得るための より良い直観を得るのに 役立ちます 今日の質問は SwiftUIが あなたのコードを見るとき 何が見えるでしょうか 答えは3つあります アイデンティティ ライフタイム そして依存です アイデンティティとは SwiftUIが Appの複数の アップデートに渡って同じまたは 異なる要素を認識する方法です ライフタイムはSwiftUIがビューやデータの存在を 時間軸で追跡する方法です そして依存関係は SwiftUIがインターフェースの 更新が必要な時期とその理由を理解する方法です この3つのコンセプトはSwiftUIが何をどのように いつ変更する必要があるかを決める方法を示し その結果 ダイナミックな ユーザーインタフェースが実現します 今日は それぞれのコンセプトについて より深くお話します まず アイデンティティから始めましょう ここに数人の友達が手伝いにきてくれています このRuby Spanielsは愛らしいのですが 深い哲学的な質問の対象でもあります この2匹は別の犬ですか? それとも 同じ犬の写真が2枚あるのでしょうか? 実際のところ それを断言するのは不可能です 情報が足りないからです この物事が「同じか違うか」という 問題は「アイデンティティ」と 呼ばれるものの核心です アイデンティティが重要なのは 犬だけではありません SwiftUIがAppを どのように理解するか 重要な側面でもあります 例を見てみましょう これは私が作った「Good Dog Bad Dog」 というAppで毛むくじゃらの友人が 最高にお行儀よくしているかどうかを 記録するのに役立ちます とても簡単です 画面のどこかをタップするだけで 良い状態と悪い状態を 切り替えることができます アイデンティティが私のAppと どう関係があるのでしょうか? 実は 先ほどの犬に関する 哲学的な質問と似ているのです これらのアイコンを見ると全く異なる 2つの別の表示のように見えませんか? あるいは 同じ表示で 位置や色が違うだけなのでしょうか? この違いは インタフェースが ある状態から別の状態へと 移行する方法を変えるため 実際には非常に重要です これらのアイコンが実際に 異なる表示であるとします つまり アイコンはフェードイン フェードアウトのように 独立して移行する必要があるということです しかし もしこれらが実際には 同じ表示だとしたら? そうすると 同じビューが ある場所から 別の場所に移動することになり 移行中に画面上でビューがスライドする 必要があることを意味します そのため 異なる状態間でビューをつなぐことは SwiftUIがそれらの間でどのように移行するかを 理解するために重要です これがビューアイデンティティの 重要なコンセプトです 同じアイデンティティを共有する ビューは 同じコンセプトの UI要素の異なる状態を表します 一方で異なるUI要素を表すビューは 常に異なるアイデンティティを持っています 後半では ビューのアイデンティティが Appのデータや更新サイクルに 与える実質的な影響について LucaとRajが説明します 今は SwiftUIで使用される 2つの異なるタイプのアイデンティティが コードでどのように表現されるかを 見てみましょう まず 明確なアイデンティティです カスタムまたは データで決まる識別子を使用します 2つ目は 構造的なアイデンティティです ビューのタイプと階層内の位置によって ビューを区別します さて これらのコンセプトを理解するために さらに友人を紹介しましょう 犬を見分けるのは 特に同じように見える場合は難しいものです では どんなプラスアルファの情報があれば 自分の犬を特定することが できるのでしょうか? 1つの方法は 単純に名前を聞くことです 見た目が同じで 名前も同じなら 同じ犬である 可能性は高いと思います しかし 名前が違う場合は 実際に違う犬であることが 保証されます このように名前や識別子を付けることは 明確なアイデンティティの形です 明確なアイデンティティは強力で 柔軟性がありますが どこかで誰かが これらの名前を すべて記録しておく必要があります 明確なアイデンティティの1つの形態として ポインタアイデンティティがあり UIKitやAppKitで 使用されているので既に 慣れていらっしゃるかもしれません 現在 SwiftUIはポインタIDを 使用していませんが それについて学ぶことで SwiftUIの動作が どのように異なりなぜ異なるかを より理解するのに役立ちます ちょっと見てみましょう 同様のUIKitやAppKitの ビュー階層を考えてみましょう UIViewsとNSViewsはクラスなので そのポインタを使ってメモリを割り当てています このポインタは 明確な アイデンティティの本来の源です ポインタを使用するだけで 個々のビューを参照でき 2つのビューが同じポインタを 共有している場合 それらが本当に同じビュー であることを保証できます しかし SwiftUIのビューはクラスではなく 構造体として一般的に 表わされる値型であるため SwiftUIはポインタを使用しません 2019年の講義「SwiftUIの基本」では SwiftUIが ビューにクラスではなく 値型を使う理由について説明していますので 詳細はそちらをご覧いただく ことをお勧めします 今のところ 知っておくべき重要なことは 値型は SwiftUIが そのビューのための 永続的なアイデンティティとして使用できる 基準を満たしていないということです 代わりにSwiftUIは他の形式の アイデンティティに依存しています 例えば 以下のリストを考えてみましょう 使用されているidパラメータは アイデンティティの一形態です 各救助犬のタグIDはリスト内の対応するビューを 明確に認識するために使用されます 救助犬のコレクションが変更された場合 SwiftUIはこれらのIDで何が変更されたかを 正確に理解して リスト内で正しいアニメーションを 生成することができます この場合 SwiftUIは異なる セクション間を移動するビューを 正しくアニメーション化することができます もっと高度な例を見てみましょう ここでは ScrollViewReader で 下記のボタンを使ってビューの 上部にジャンプしています id修飾子は カスタム識別子を使用して ビューを明確に識別する方法を提供します ここでは ページ上部の ヘッダービューを指定しています そして その識別子を スクロールビュープロキシの scrollToメソッドに渡して SwiftUIに特定のビューに 移動するよう伝えることができます この方法の優れた点は 全てのビューを 明確に識別する必要がなく ヘッダーテキストのように コードの他の場所で 参照する必要があるものだけを 識別することができることです それに比べて ScrollViewReader ScrollView Text および Button には明確な識別子は必要ありません しかし アイデンティティが 明示されていないからと言って これらのビューにアイデンティティが 無いわけではありません たとえ明示されていなくても すべてのビューに アイデンティティがあるからです ここで構造的なアイデンティティの出番です SwiftUIは ビュー階層の構造を利用して あなたがしなくてもよいようにビューの暗黙の アイデンティティを生成します さて 私が言っていることを説明するために もう何人かの友人を連れてきましょう 例えば 似たような犬を2匹飼っていて 名前は分からないが それぞれを識別する必要があるとします さて 仮にこの犬たちがとても優秀で じっとしていられる犬だとします 動かないと保証されている場合は 「左の犬」「右の犬」というように 座っている場所だけで識別することができます 被写体の相対的な位置を利用してお互いを 区別しているのですが これが構造同一性です SwiftUIは そのAPI全体で 構造同一性を利用しており 典型的な例は ビューコード内で「if」文や 他の条件付きロジックを使用する場合です 条件文の構造を見れば それぞれのビューを 明確に特定することができます 最初のビューは 条件が真のときのみ表示され 2番目のビューは条件が偽のときのみ表示されます つまり 似たような外観であっても どのビューがどのビューなのかを 常に見分けることができます しかし これはSwiftUIが これらのビューがその場所に留まり 決して場所が入れ替わらないことを 保証できる場合にのみ機能します SwiftUIは ビュー階層の型構造を見ることで これを達成します SwiftUIがビューを見るときには その一般的なタイプを見ます このケースでは「if」文が a_ConditionalContent ビューに変換され 真と偽のコンテンツの一般的な型になっています この変換は Swiftのリザルトビルダの 一種であるビュービルダによって行われます ビュープロトコルは 暗黙のうちに そのボディプロパティを ビュービルダで包みプロパティ内の ロジックステートメントから 単一のジェネリックビューを構築します bodyプロパティの一部のビュー戻り型は この静的な複合型を表すプレースホルダで コードを混乱させないように隠しています この一般的な型を使用することで SwiftUIは 真のビューが常に AdoptionDirectoryであることを保証し 偽のビューが常にDogListであることを保証し 舞台裏で暗黙の安定したアイデンティティを 割り当てることを可能にしています 実はこれが 先程の「Good Dog Bad Dog」の Appを理解する上でのポイントになります 上のコードでは条件分岐ごとに 異なるビューを定義する「if」文があります これにより SwiftUIは「if」文の各分岐が 異なるアイデンティティを持つ 別のビューを表しているため これによりビューが移行します 代わりに レイアウトと色を変更する PawViewを一つ持つこともできます 異なる状態に移行するとビューはスムーズに次の 位置にスライドします これは 一貫したアイデンティティを持つ 単一のビューを変更しているからです これらの戦略はどちらも機能しますが SwiftUIは 通常2番目のアプローチを推奨します デフォルトではアイデンティティを維持し より流動的な移行を行うようにしてください これは ビューのライフタイムと 状態を維持するのにも役立ちますが これについては後ほどLucaが詳しく説明します 構造同一性について理解したので その邪悪な敵であるAnyViewについて 話す必要があります AnyViewを使用による影響を理解するために ビューの構造に与える 影響を見てみましょう 先程AdoptionDirectoryと DogListを切り替えるために この「if」文を書きました SwiftUIがこのコードを見ると 右のような一般的な型の構造が見えます 今度は AnyViewを多用した 別の例を見てみましょう これは 犬の品種を表すビューを 得るために書いたヘルパ関数です 関数内の各条件分岐は 異なる種類のビューを返すので Swiftは関数全体に対して 単一の戻り型を必要とするため それらすべてをAnyViewsで ラップアップしました 残念ながら これはSwiftUIが 私のコードの条件付き構造を 認識できないことも意味します 代わりに AnyViewを 関数の戻り型として見ます これは AnyViewが 「型消去ラッパー型」と呼ばれるもので ラップしているビューのタイプを その一般的特徴から隠しています しかし 恐らくもっと重要なことは このコードは私たち人間にとって 本当に読みにくいということです このコードを単純化しその構造をSwiftUIに 表示することができるかどうかを見てみましょう まず このブランチでは近くに羊がいる場合 BorderCollieViewの横に SheepViewを条件付きで追加しているようです ビューの周りにHStackを 条件付きで追加するのではなく HStackの中にビューを 条件付きで追加することで これを単純化することができます この変更により 各ブランチから 単一のビューを返しているだけ であることが明らかになり ローカルのdogView変数は必要なくなりました 代わりに 各ブランチ内にあるリターン文で 置き換えることができます 先程見たように 通常のSwiftUIのビューコードは 異なるタイプのビューを返す 「if」文を使うことができます しかし 単にコードからリターン文と AnyViewを削除してみると いくつかのエラーと警告が表示されます これは SwiftUIがヘルパ関数から単一の戻り型を 必要とするためです では どのようにしてこれらの エラーを回避できるでしょうか? ビューのbodyプロパティは特別なもので ビュープロトコルが暗黙のうちに ViewBuilderでラップしているからです これにより プロパティのロジックを 単一の汎用的なビュー構造に変換します さて Swiftはデフォルトでは ヘルパ関数がビュービルダで あるとは推測しませんが 手動でビュービルダの属性を 自分で適用することで それを選択することができます これにより 警告やエラーを出さずに リターン文やAnyViewラッパーを 削除することができます さあ コードがいい感じになってきましたね AnyViewをすべて削除して 以前よりも読みやすくなりました そして 結果のタイプシグネチャを見ると 条件付きコンテンツのツリーを持つ関数の 条件付きロジックを正確に再現しており SwiftUIにビューとそのコンポーネントの アイデンティティを提供します しかし もう1つ改善できる小さな点があります この関数のトップレベルでは 犬の品種の異なるケースを 照合しています これは ビュービルダでもサポートされている Switch文の優れた使用例のように思えます これで ビューの様々なケースを 素早く理解できるようになりました また Switch文は 単なる条件文の構文解析であるため 結果として右図のビューのタイプシグネチャは 全く同じままです 先日 AnyViewコードから 型情報を消去する方法を示して ビュービルダを利用して 不要なAnyViewを削除する方法を 紹介しました 一般に 可能な限りAnyViewを避けることを お勧めします AnyViewの数が多すぎるとコードが読みにくく 理解しにくくなります 「if」もしくは「else」またはswitchなどの 従来の制御フロー文を使用すると ビューの様々な可能性のある状態を より簡単に確認することができます また AnyViewは静的な型情報を コンパイラから隠してしまうため 診断に役立つエラーや警告が コードに現れないことがあります 最後に 必要のない時にAnyViewを使用すると パフォーマンスが低下する可能性が あることを覚えておいてください 可能であれば コードにAnyViewを渡すのではなく 静的な型情報を保持するために ジェネリックを使用してください これで SwiftUIにおける ビューのアイデンティティの 基本的な型の紹介が終わりました 明確なアイデンティティでは ビューのアイデンティティを データに結び付けたり 特定のビューを参照するために カスタムの識別子を提供することができます また 構造同一性では 型とビュー階層内の位置に基づいて SwiftUIがどのようにビューを識別するかを 学びました ここからは ビューのアイデンティティが 寿命と状態にどのように 関連しているかを説明します ありがとう Matt SwiftUIがビューを識別する方法が分かったので アイデンティティがビューとデータの寿命に どのように結びつくかを探ってみましょう これは SwiftUIがどう機能するか より理解するのに役立ちます これを説明するために 友人にも登場してもらうつもりです Theseusです 彼もかわいいでしょう? もっとかわいいと言う人もいるでしょうね 余談ですが 私たちは 自分の好きなペットに名前を付けたら その子が違う状態になって 一日中動きまわっていても いつも同じかわいい猫だと思うのは とても直観的なことです 私たちが見ると 彼は眠そうに しているかもしれないし その次の瞬間には ちゃんとした 猫として私の存在を迷惑がっている かもしれません でも 彼は常に Theseusであり続けます これがアイデンティティと寿命を 結びつけることの本質です アイデンティティによって 時間の経過とともに異なる価値観に対して 安定した要素を定義することができます 言わば それは時間の経過とともに 私たちに継続性を取り入れさせます SwiftUIにどう当てはまるのかと 思うかもしれません そこで Mattが取り組んでいた 猫向けのAppに戻りましょう Theseusが 様々な瞬間に様々な状態にある 可能性があるように私たちのビューも また その生涯を通じて異なる状態にあります それぞれの状態は ビューにとって 異なる価値があります アイデンティティはこれらの異なる価値を 時間の経過とともに1つの実態-- ビューとして結びつけます このことを明確にするために いくつかのコードを見てみましょう ここでは 鳴き声の大きさを表す シンプルなビューをご紹介します ネタバレになりますが Theseusは結構うるさいです bodyの評価により SwiftUIはこのビューに新しい値を作成します この場合は 強度値25です Theseusはお腹が空いてきて 気づいてほしいと思っています bodyが より高い強度で再度呼び出され ビューの新しい値が作成されます これらは 同じビュー定義から作成された 2つの異なる値です SwiftUIは 比較を実行し ビューが変更されたかどうかを 知るために 値のコピーを保持します しかし その後 その値は破棄されます ここで理解しておきたいのはビューの値は ビューのアイデンティティとは 異なるということです ビューの値は一時的なものでその存続期間に 依存するべきではありません しかし アイデンティティーは コントロールすることができます ビューが最初に作成され 表示されるとき SwiftUIは前に説明した 技術の組み合わせを使って それにアイデンティティを割り当てます 時間の経過とともに更新によって駆動され ビューのための新しい値が作られます しかし SwiftUIの視点では これらは同じビューを表します ビューのアイデンティティが変更されるか ビューが削除されると その寿命は終わります ビューの寿命とは そのビューに関連する アイデンティティの存続期間を意味します ビューのアイデンティティと その存続期間を結びつけることは SwiftUIがどのように状態を持続させるかを 理解する上で基本となります ではStateとStateObjectを図に入れてみましょう SwiftUIがあなたのビューを見て StateまたはStateObjectを確認した時 ビューの存続期間を通じてす そのデータを永続化する必要が あることが分かります つまり StateとStateObjectは ビューのアイデンティティに関連する 永続的なストレージなのです ビューのアイデンティティの始まりでは ビューが最初に作成されたとき SwiftUIはStateと StateObjectのためにそれらの初期値を使用して メモリ内のストレージを割り当てようとします ここでは タイトルの状態に焦点を当てています ビューの寿命を通してSwiftUIは 変異し ビューのボディが再評価されると このストレージを永続化します アイデンティティの変更が 状態の永続化にどう影響するか 具体的な例を見てみましょう これは 同じビューが2つの別々の ブランチにあるという 興味深い例です 以前の記憶では 構造同一性のため2つのビューは 異なるアイデンティティを 持つと考えられています Mattはこれがアニメーションに どう影響を与えるかを説明しましたが これは状態の永続性にも 大きな影響を与えます 実際に見てみましょう 最初にbodyを評価し真のブランチに入った時 SwiftUIは 初期値で状態に 永続的なストレージを割り当てます このビューの存続期間を通してSwiftUIは 様々なアクションによって 変更されても状態を維持します しかし dayTimeの値が変更され 偽のブランチに入った場合はどうなるでしょうか? SwiftUIは これが明確なアイデンティティを持つ 別のビューであることを認識しています 状態の初期値から始まって偽のビューのために 新しいストレージを作成し 真のビューのためのストレージは 直後に解除されます しかし 本当のブランチに 戻ったらどうなるでしょうか? それはまた新しいビューなので SwiftUIは新しいストレージを作成し 状態の初期値からやり直します ここでのポイントは アイデンティティが変わる度に 状態が置き換えられるということです ここで少し立ち止まって この重要なポイントを確実に理解しましょう 状態の持続性は ビューの存続期間と結びついています これは非常に強力な概念で ビューの本質である状態を 明確に分離し それをアイデンティティに 結びつけることができるからです 他のすべてのものは そこから導き出すことができます データは非常に重要であるため SwiftUIには データのアイデンティティを ビューの明確なアイデンティティの形式として 使用する 一連のデータ駆動型構造があります その典型的な例がForEachです ここでは ForEachを初期化する 様々な方法を見てみましょう これにより このタイプについての 理解を深めることができます ForEachのもっとも単純な形式は 一定の範囲を取る形式です これは 特に新しいUIのプロトタイピングを 始める場合に 非常に便利な 初期化の方法です SwiftUIは ビュービルダによって 生成されたビューを識別するために この範囲のオフセットを使用します 一定の範囲を必要とすることで ビューの存続期間はアイデンティティが 安定していることを保証します 実際 動的な範囲でこのイニシャライザを 使用することはエラーとなります また 今年の新機能として 一定でない範囲を指定すると 警告が表示されます もっと面白くするために 動的なデータのコレクションを 取り入れてみましょう このイニシャライザはコレクションと 識別子となるプロパティのキーパスを 受け取って識別子として機能します SwiftUIは コレクションの要素から生成された 全てのビューに アイデンティティを割り当てるため その値を使用するので このプロパティはハッシュ化 可能でなければなりません 後でRajが 安定したアイデンティティを 選択することが Appのパフォーマンスと正確さに どのように影響するか 幾つかの例を示してくれます データに安定したアイデンティティを 提供するという考えは非常に重要で 標準ライブラリでは この機能を説明するための 識別可能なプロトコルを定義しています SwiftUIはこのプロトコルを最大限に活用し キーパスを省略して プロトコルの要件で提供される識別子を使用して データやビューに関連付けられた アイデンティティを 定義することができます 私がSwiftで本当に気に入っているのは その型システムを利用して 解決しようとしている問題の制約を 正確に描写できることです それでは ここで使用している イニシャライザの定義を見てみましょう この短い定義の中には興味深いものが たくさんありますので それを紐解いていきましょう ForEachは主に2つの要素を必要とします コレクションと (ここでは一般的な引数データで示される) コレクションの各要素から ビューを作成する方法です このイニシャライザの形状からForEachが データのコレクションと ビューのコレクションの関係を 定義していることが直観的に分かると思います しかし 実際にはここで最も興味深いのは コレクションの要素を識別可能なものに 限定していることです 繰り返しになりますが 識別可能なプロトコルの目的は SwiftUIがデータの存続期間を通じて データを追跡できるように 型がアイデンティティの安定した コンセプトを提供できるようにすることです 実際 これは以前にお話しした アイデンティティと寿命の概念と 非常によく似ています 認識可能型とビュービルダを 取るSwiftUIのビューは データ駆動型のコンポーネントです これらのビューは 関連するビューの 存続期間をスコープするために 提供されたデータの アイデンティティを使用します 良い識別子を選ぶことは ビューやデータの寿命を コントロールするチャンスです では このセクションで説明した ことを振り返ってまmしょう ビューの値は一過性のものであり その寿命に依存するべきではありません しかし 識別子はそうではなく 時間とともに継続性を与えるものです ビューのアイデンティティを制御し 状態の存続期間を 明確にスコープするために アイデンティティを使用することができます 最後にSwiftUIはデータ駆動型の コンポーネントのための 識別可能なプロトコルを最大限に活用するので データのために安定した識別子を 選択することが重要です そして今 引き継いで Rajに渡したいと思います ありがとう Luca これまでに 私たちは アイデンティティとは何か それがどうにしてビューの存続期間に 関係するかを説明してきました 次は SwiftUIがどのように UIを更新するかを説明します 目標は SwiftUIコードを構造化する方法の より良いメンタルモデルを提供することです また 最後にいくつかの例を挙げて 全てを説明します 依存関係の説明を始めるにあたって ビューを見てみましょう これがシンプルなビューです これは 犬におやつを与える ボタンを表示しています すみません Luca でも私はもっと犬の人です それでは ビューの構造に注目してみましょう まず トップを見てみましょう 2つのプロパティがあります 1つは犬 もう1つは おやつのプロパティです これらのプロパティは ビューの依存関係です 依存関係とは ビューへの入力に過ぎません 依存関係が変化すると ビューは 新しいbodyを生成する 必要があります bodyは ビューの階層を 構築する場所です このビューの階層に入ると アクション付きのボタンがあります アクションは ビューの依存関係に 変化をもたらすものです コードを図に置き換えてみましょう これは DogViewの図です ボタンをタップすると 犬に報酬を与えるアクションが実行されます 犬はあっという間におやつを 飲み込んでしまいました そして その結果は犬に変化をもたらします もう1つ欲しくなるのではないでしょうか 依存関係が変わり DogView は新しいbodyを生成します SwiftUIにおけるデータフローの 一般的な概念については WWDC 2020の 「SwiftUIにおけるデータの重要事項」を ご覧ください 次に この図を少し簡略化してみましょう ビューの階層に注目するとビューがツリー状の 構造を形成していることが分かります また 犬とおやつの依存関係をトップに戻すと やはり「木」に見えます しかし 依存関係を持つビューは DogViewだけではありません SwiftUIでは 各ビューは 独自の依存関係をセットで持てます まだ「木」のように見えます しかし 注意してほしいのは 同じ状態や他のデータに依存する 複数のビューが存在することです 例えば 子孫の一人も犬に 依存している可能性があります そして 他の依存関係の1つでも このようなことが起こりえます 最初は「木」だったのですがこの構造は 今では 大まかに「木」に似ているだけです 実際 線が重ならないように再配置すると この構造になります これは 実際には「木」ではなくて グラフであることを示しています 実はこの構造を「依存グラフ」と呼んでいます この構造が重要なのはSwiftUIが新しいbodyを 必要とするビューのみを 効率的に更新することを可能にします 例えば 下部の依存関係を見てみましょう この依存関係を調べてみると 2つの依存するビューがあります グラフの秘密は依存関係が変更された場合 それらのビューだけが無効になることです SwitchUIは各ビューのbodyを呼び出し それぞれ新しいbodyの値を生成します SwiftUIは 無効になった 各ビューのbodyの値をインスタンス化します その結果 より多くの依存関係が 変更されるかもしれませんが 必ずしもそうではありません ビューは値型なので SwiftUIは効率的に比較して 適切なサブセットだけを更新することができます これは先程Lucaが話したことを 別の角度から見たものです ビューの値は短命です 構造体の値は比較のために使われているだけで ビュー自体の寿命はもっと長いのです これにより 中央のビューに 新しいbodyを生成することを 避けることができるのです アイデンティティは 依存関係グラフのバックボーンです Mattが言ったように 明確的 または構造的に指定されているか どうかに関わらず 全てのビューは アイデンティティを持っています そのアイデンティティは SwiftUIが変更を 適切なビューにルーティングし 効率的にUIを更新する方法です 依存関係には多くの種類があります 以前 「おやつ」プロパティと 「犬」バインディングで いくつかの例を見ましたが 環境 状態 または 監視可能なオブジェクトに対する プロパティラッパーの いずれかを使用して依存関係を 形成することもできます 次に ビューでのアイデンティティの使い方を 改善する方法についてお話ししたいと思います これで SwiftUIがコードを より理解できるようになります Lucaが言ったようにビューの寿命は そのアイデンティティの期間であり 識別子の安定性が重要であることを意味します 安定していない識別子は ビューの寿命を短くする可能性があります そして 安定した識別子を持つことは SwiftUIが継続的に ビューのためのストレージを作成し グラフを更新する必要がないため パフォーマンスにも役立ちます 先ほど見たようにSwiftUIは寿命を使って永続的な ストレージの管理をしているので 安定した識別子は 状態の損失を避けるためにも重要です 識別子の安定性の重要性を説明するために コードの例を見てみましょう この例では 私の好きなペットのリストを使います ペット構造体には識別子が付いています しかし 実はバグがあります 新しいペットを飼い始める度に 画面上の全てが点滅してしまいます ちょっと立ち止まって このコードを見てみましょう どこにバグがあるか分かりますか? バグはここ 識別可能な コンフォーマンスにあります もしテストに合格しなくても心配はいりません このセクションには何の問題もありません 問題は この識別子が安定していないことで データが変更される度に 新しい識別子を得ることになります 代わりに petsの配列の インデックスを使ってみましょう 残念ながら これにも同様の問題があります 指数を使うことでビューはコレクションの中の それぞれのペットの位置で 認識されるようになります もし私が新しくお気に入りのペットを 決めたら 他の全てのペットの アイデンティティが変わってしまい 悪いバグが発生する可能性があります この例では ボタンを押すと インデックス0に新しい要素が 挿入されますが最後のインデックスが新しいため 最初ではなく最後に挿入されることになります これは 計算されたランダムな識別子のように インデックスは安定した形の アイデンティティではないからです この例では データベースからのものや ペットの安定した特性から得られるものなど 安定した識別子を使用する必要があります 持続する識別子を使うのが良いでしょう これでアニメーションは 素晴らしいものになりました
しかし 良い識別子に必要な特性は 安定性だけではありません 良い識別子のもう1つの特性は 「ユニークさ」です それぞれの識別子は 単一のビューに 対応していなければなりません これにより アニメーションの 見栄えが良くなり パフォーマンスがスムーズになり 階層の依存関係が 最も効率的な形で反映されます もう1別の例を見てみましょう この例では 私のペットの お気に入りのおやつを集めた ビューを作っています それぞれのおやつには 名前 絵文字 そして賞味期限があります それぞれのおやつを名前で 識別することにしました この時点で察しが付くと思いますが ここにもバグがあります 同じ種類のお菓子が 2つ以上あるとどうなるか? あなたはどうか分かりませんが 私は 犬用のビスケットを まとめて買うのが好きです ジャーに追加すると表示されないことがあります 問題は おやつの名前がそのおやつ 固有の識別子ではないということです 代わりに シリアル番号やその他の固有のIDを おやつごとに使用することができます そうすれば正しいデータが表示されます また より良いアニメーションと より良いパフォーマンスも期待されます SwiftUIが識別子を必要とする時 助けが必要です 特に計算されたプロパティで ランダムな識別子を使用する場合は 注意が必要です 一般的に すべての識別子は 安定していなければなりません 識別子は時間の経過とともに 変化してはなりません 新しい識別子は 新しいアイテムを 新しい寿命で表します そして最後に 識別子は 一意でなければなりません 複数のビューが1つの識別子を 共有することはできません SwiftUIは Appをスムーズにバグなく 動作させるために これらの プロパティに依存しています さて 明確なアイデンティティ について話しましたが 構造同一性に移りたいと思います この例では 先ほどのおやつの瓶に 取り組んでいます 責任あるペット愛好家として 自分のペットには 賞味期限の切れていない 最高の食品しか与えません おやつが有効期限切れで 悪くなったことを見分けるために 新しい修飾子を追加しました 淡色表示されているセルをハイライトしました では 修飾子を見てみましょう 修飾子の中で 日付を設定し現在の日付と比較して 表示を暗くするタイミングを 確認しているのが分かります 一見するとこれでいいように見えますが ここには微妙な問題があります 条件が変わり おやつが期限切れになった場合 ここにはブランチがあるため 結局新しいIDが必要になります Mattが言ったようにブランチは 構造的なアイデンティティの一形態です つまり コンテンツのコピーが 1つではなく2つあり 任意に変更できるということです ここでのブランチは修飾語の中に あることに注意してください 分かりやすくするために 修飾子と その使用サイトを同じスライドに 載せましたが あなたのプロジェクトでは 気づかないうちにファイル間で このような分岐が行われているかもしれません もちろん ここで説明したことは すべてビューとビュー修飾子にも当てはまります では どうすればこのような事態を 避けることができるのでしょうか? 1つの方法は このようにブランチを折りたたんで 不透明度修飾子の中に条件を移すことです このブランチを削除することで このビューは単一の アイデンティティを持つものとして 正しく描写されています さらに 条件を不透明度修飾子の中に移すと パフォーマンスが向上します 依存するコードをしっかりスコープしたからです これで 条件が変わっても 不透明度だけど変えればよいことになります この秘訣は 条件が真の場合 不透明度が1になることです 不透明度1は効果がありません このような修飾子を「不活性修飾子」と呼びます レンダリング結果に影響を与えないからです SwiftUIの修飾子は安価なので このパターンには本質的なコストは 殆どありません 結果として視覚的な効果がないため フレームワークは効果的に修飾子を削除し そのコストをさらに減らすことができます ブランチは素晴らしいですが 理由によりSwiftUIに存在しています 不必要に使用されるとパフォーマンスの低下 意外なアニメーション そしてLucaがお見せしたように 状態の喪失さえも引き起こしかねません ブランチを取り入れる際には ちょっと立ち止まって 複数のビューを表現しているのか 同じビューの2つの状態を表現して いるのかを考えてみてください 先ほど見たように 単一のビューを識別するためには ブランチの代わりに不活性修飾子を 使う方が上手くいくことがあります ここでは 不活性修飾子の例を いくつかご紹介します 私が気に入っているのは条件付きで 環境に書き込むことができる transformEnvironmentです 以上のことから 本日はアイデンティティが 優れたパフォーマンスの 秘訣の一つであることをご紹介しました これまでに明確なアイデンティティと 構造的なアイデンティティに ついて説明しそれぞれをどのように活用して Appを改善できるかを説明してきました アイデンティティからは 関連する ストレージや移行などを制御する ビューの寿命を導き出すことができます SwiftUIはアイデンティティと寿命を 使用して依存関係を形成し それがグラフで表現されて UIを効率的に更新できることを説明しました SwiftUIを解説するとともにAppのバグを回避し パフォーマンスを向上させるための ヒントやコツをお伝えしました そして これらの秘訣を学んだ今 あなたのコードを見て回り それらが役立つかどうかを確認してください ありがとうございました 素晴らしいAppを作って下さい ♪
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。