ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
SwiftUIでカスタムビューを構築する
このセッションでは、高度なコンポジション、レイアウト、グラフィックス、アニメーションを使用して、SwiftUIでカスタムビューとコントロールを構築する方法を紹介します。パフォーマンスが高くアニメーション化が可能なコントロールのコードが手順を追って作成される様子をご確認いただくことで、SwiftUIのレイアウトシステムについて詳しく理解していただけます。
リソース
関連ビデオ
WWDC22
WWDC20
WWDC19
-
ダウンロード
(音楽)
(拍手) こんにちは またお会いできてうれしいです 私はデイブです
SwiftUIを使うと アプリケーションを簡単に 組み合わせることができます ジョンと私が これからお話しするのは 洗練されたレイアウトやグラフィック アニメーションを備えた― 機能的アプリケーションの作り方です
まずはSwiftUIの 2つのサブシステムを紹介し ジョンがそれを使って カスタム制御を構築します 始めましょう SwiftUIはまず レイアウトのシステムから始まります プレビューエディタのテキストの 青い枠は範囲を示します レイアウトとは画面上のものの 範囲を決めることです 例を使ってレイアウトの内幕を 見ていきましょう
作業の際に表示されるのは3つです ビュー階層の下部にあるTextと
ボディであるTextと 同じ範囲のContentView そしてRoot Viewです この例ではデバイスの寸法から セーフゾーンを除いた部分です このようなスマホの上の部分は 含まれません
補足ですが この修飾子を使えば その部分にも設定可能ですが デフォルトはセーフゾーン内です ボディのあるビューの一番上のレイヤは いわゆる中立のレイアウトです その範囲はボディの範囲で決まり 同様に動作するので レイアウトを行う際には 同じビューとして扱います 従って注目すべきビューは 実質2つです レイアウトは3段階で行います
まず Root Viewで サイズが提案されます 2つの矢印で示されます これが全体のセーフゾーンになります
“ご親切にありがとう でもこの大きさで大丈夫”と Textが答えます SwiftUIでは子ビューのサイズは 親ビューに左右されません
“君をとりあえず真ん中に入れよう”と Root Viewが言います
これだけです 今回は簡単な例でしたが どんなレイアウトでも親ビューと 子ビューのやり取りは同じです 全体のレイアウトはこれらの 親子のやり取りから生まれます
注目すべきは2つ目の段階です これまでとは異なるため 重要な意味をもっています ビューのサイズ設定ができるのです
ビューごとに設定できるので サイズ変更の方法や タイミングも自由です 例えばこのビューが 50×10ポイントの固定なのは Root Viewのサイズ枠が 固定だからです
このビューはサイズが自由ですが 高さと幅の比は同じで アスペクト比は常に1対1です ビュー定義に サイズ設定が含まれるのです
Textについても同じで Textの範囲は 表示された線を出ることはありません これについては後ほど説明します
レイアウトの最後の段階は UIの見栄えを良くする重要なものです 自動的に行われるので 気にかける必要はありませんが SwiftUIではビューの角を 最も近いピクセルに調整します アンチエイリアスではなく エッジが常に鮮明です 優れたアプリケーションに 必要なことの1つに過ぎませんが SwiftUIはこれを実現しました (拍手) アプリケーションの差別化に 集中できます 基本が分かったところで 他にできることがないか 見てみましょう
例のテキストを変えてみます 例えばアボカドトーストにしてみます お腹がすいてきませんか? もう少し魅力的にしてみましょう 背景を緑にします この背景修飾子は Backgroundの中のTextを 二次的な子ビューの Colorで覆います
その結果 緑の背景は テキストの範囲と一致します 補足ですが プレビューができない場合は ビューに背景色や境界色をつけると ビューの範囲が分かります 次に テキストの周囲に もう少しスペースを入れたいので 少しパディングを入れます
SwiftUIはプラットフォームや Dynamic Typeのサイズや環境に合わせて パディングの量を決定します パラメータが未入力の場合に 適応したパディングが行われるように SwiftUIはピッカーやボタンを 状況に合ったスタイルにします テキストの端に沿ったパディングを 行うこともできます
適応型の修飾子は レイアウト調整に最適な方法です コードの複雑化やプロセスの初期段階に 時間を取られることや ハードコーディングを 避けられるからです
今回は詳細を管理したいと思います 全ての端からパディングを 10ポイント行う仕様だとします こう明示することができます “Hello World”よりも 少し面白いこの例で レイアウトのプロセスを 見ていきましょう
まずRoot ViewがBackgroundに対する サイズを提案します Toastのビューと同じく Backgroundも中立のレイアウトで 提案されたサイズは Paddingにも伝達されます
Paddingは子ビューの端に 10ポイントを追加するので その分だけ小さい範囲を 子ビューに提案します
Textは必要な幅を Paddingに伝達し Paddingはそれより 10ポイント大きい範囲を取り テキストを 適切な位置に設定します
Backgroundは中立のレイアウトなので サイズを伝達しますが その前に二次的な子ビューの Colorにサイズを提案します
レイアウトにおいて Colorは非常に従順で 提案されたサイズに従います Colorのサイズは Paddingと同じです 最後にBackgroundが Root Viewにサイズを報告し
Root Viewが中央に設定されます 先ほどよりも単純ですが 重要な例を見てみましょう
ビューのボディのサイズが 20×20に固定された画像です SwiftUIではAsset Catalogやコードで サイズ変更可能と設定しなければ サイズは固定です 全体を1.5倍の大きさにしたいので このように30×30の フレーム修飾子を追加します
お気づきになったかもしれませんが このおいしそうな画像の サイズは変わっていません
申し上げたとおり 固定サイズなので当然です 画像の周囲には30×30の枠があります これがボディのサイズなので ビューは修飾子を追加する前よりも 50%大きくなっています
枠のサイズと画像のサイズが 一致しないのは矛盾でしょうか?
違います レイアウトのシステムは 正しく機能しています SwiftUIにおいてフレームは 制約を受けません 額縁のようなただのビューなのです 子ビューに対して 固定の寸法を提示しますが 他のビューと同様に サイズを決定するのは子ビューです つまりSwiftUIのレイアウトは 使い心地が軽いのです その結果 制限不足だったり 過度に制限するシステムがなく 表現すること全てに 明確な効果があるのです 結果に不満足な場合を除いて 間違ったレイアウトは存在しません
基本が理解できたところで スタックについてお話しましょう HStackは行で VStackは列で 子ビューを整理します 私は4つのスタックと数行のコードで このリストのセルをまとめました
これがコードです 一番上がHStackで
2つある子ビューの1つ目は 星評価を含んだVStackです
もう1つの子ビューもVStackで 2つの子ビューが整列しています
そのうちの1つはHStackで タイトルと自由なスペースと アボカドの画像を含んでいます
スタックが4つです 元に戻しましょう
さて― (拍手)
どのスタックの子ビューも 別の子ビューに重なりませんでした adaptive spacingが スペースを確保しているからです 隣接するテキストの ベースラインの間隔は AppleのHuman Interface Guidelineと 完全に一致しています ベースラインと端部の間も同様です Guidelineのルールが エンコードされているのです
最も単純で簡単なコードでも 素晴らしい結果を生み出すことが できるようになってきています
いつもどおりに制御したい場合は 希望どおりの結果を得られるように することもできます
SwiftUIにできることは他にもあります アラビア語のように逆に書く言語の アプリケーションで システム言語を変更する場合 SwiftUIが水平座標を入れ替えます 再コード化は不要です (拍手) 文字を書く方向ではなく 配置についての話をするのは 国際化対応が自動的に 行われるからです
次に スタックのレイアウトを 見てみましょう ここまでのビューには 線状につながった子ビューがありました しかしスタックの子ビューは 対等にスペースを奪い合います このスタックのテキストは 1行に収めなければいけないとします
つまりスタックのスペースが 小さくなると
テキストを省略しなければなりません 今回はスペースが十分ある場合について 考えてみましょう スタックはまず内部スペースの 要件を理解し 提案された幅からその分を減らして 割り当てられるスペースを 割り出します サイズが不明の 3つの子ビューがあります
そこで 残ったスペースを 均等に3つに分け その1つを最も柔軟性のない 子ビューのサイズとします 画像は固定サイズなので 最も柔軟性がありません
この画像に必要な スペースはこれだけです 割り当てられるスペースから 画像が必要とする分を除きます
これを繰り返します 残りは2つの子ビューです 残ったスペースを2つに分け 1つを最も柔軟性のない “Delicious”に提供します “Delicious”のスペースが 提供された分より小さいことを 覚えておいてください
割り当てられるスペースから このスペースを除き 残った分が“Avocado Toast”の スペースです 十分な量です
最後の段階です
全ての子ビューのサイズが決まったので スタックはスペースを取って 子ビューを並べます コードが配置を指定していないので デフォルトの中央揃えになっています スタックはこれを利用し 子ビューを縦方向の中央揃えにします
最後に子ビューをぴったり囲むように スタックのサイズを決定します
テキストが表示された幅よりも 広がらない理由が 視覚的に分かると思います “Delicious”が提供された 全スペースを使うと “Avocado Toast”の分が減り 省略が必要になります 全てを収めるのに十分な スペースがあるのにです
2つのテキストを比べたとき 主語の“Avocado Toast”の ほうが重要です 形容詞の“Delicious”は 犠牲にできます
従って これは好ましくない結果です 考えてみると 提供されたスペースが 理想の量よりも少ない場合でも 先ほどのような省略は あまり望まれないでしょう 提供されたスペースが 少ない場合は主語を残し 形容詞を省略するでしょう
それを実行するツールがあります “Avocado Toast”の優先順位を デフォルト値の0から1に上げるのです (拍手) 子ビューのレイアウトの 優先順位が異なるとき スタックは割り当てられたスペースから 優先順位の低い子ビューの 最小幅を確保し 残りを優先順位の高い 子ビューに分配します
ここでは“Avocado Toast”が 該当します
提供されるのは 使用可能なスペースから 画像と“Delicious”を縮小した後に 残った3つの点の幅を引いたものです
最も優先順位が高い子ビューの サイズを決定すると スタックは次に優先順位の高い 子ビューに残ったスペースを分配します
もう1つご紹介したい強力なツールが
アライメントです このスタックを下揃えにできることには 驚かれないと思います
このほうがいいですね “Delicious”のフォントサイズを 小さくした場合はどうでしょう?
アプリケーション開発者の私には 気になりませんが UIデザイナーのクラスティなら きっと嫌がります (拍手) こうやって拡大して 細かく指摘するでしょう “Delicious”のベースラインがここで 画像のベースラインはもっと下で “Avocado Toast”のベースラインは ずっと上にある 全然揃ってない どういうことだ? (笑い声) SwiftUIはこの問題を解決します 最初と最後のテキストのベースラインは 上下の関係です
ベースラインを基準に調整すれば うまく解決できます (拍手) 画像はどうでしょう? 画像にテキストはありません アライメントには 必ずデフォルト値があり この場合のデフォルト値は ビューの下端です クラスティの希望どおりになりました
そういえば… よく見てみると― 指摘される点はまだあります 視覚的なベースラインです 下端まで87.4%の 位置のラインです (笑い声) SwiftUIに指示をして対応します 画像のどの基準とテキストの ベースラインを合わせるかを指示します いい感じですね (拍手) アライメントはまだ続きます スタックの例に戻りましょう 星評価とタイトルの中心を このように揃えたいとします
この2つのテキストはビュー階層の中の 別々のブランチに入っています
一番上のHStackの子ビューの センタリングは不要です デフォルトだからです これらは既にセンタリングされています 必要なのは センタリングではなく調整で 星評価とタイトルの中心を 揃えることです 独自のアライメントが必要ですが 難しくはありません extension VerticalAlignmentの わずか6行の指示です
最初に列挙型を定義します 要件が1つのアライメントIDに 適合させます SwiftUIにデフォルト値の 計算方法を指示します
この例では何を選んでも構いません デフォルトは内部のスタックから 提示しないためです ここではデフォルトをbottomとします アライメントガイドの修飾子に 似ていることが分かると思います 最後にVerticalAlignmentの 静的インスタンスを定義します これは列挙型を引数とします
これでスタックの調整に使えます
星評価とタイトルの中心に 明示的に設定します
設定した明示的なアライメント値は ネストされたスタックの 2つのレイヤに反映され 外のHStackによる内部パーツの 調整が可能になります これがカスタムアライメントです (拍手) こだわりの強いUIデザイナーも 満足する強力なツールです 次はSwiftUIの グラフィック効果についてご説明します ジョン (拍手) ありがとう SwiftUIのグラフィックの特徴と インタラクティブな制御の 作り方についてお話しします
例えばこういうものを作ります 見覚えのある普通の 制御機能のようですが 中央にはリング状のグラフィック 下には棒グラフがあります こういうものを作るためには グラフィックやアニメーションを ある程度知る必要があります
SwiftUIを使って うまく行う方法があるので その例を見てみましょう まずは基本を確認しましょう 赤い円の描き方です 最初にカスタムビューのタイプを 作成します 入力画面はこのようになります 形と色の2つを指定すると 画面に赤い丸が表示されます
設置サイズを指定していないのは レイアウトのシステムに 依存しているからです グラフィックを描くときも 依存します
従って 図形はレイアウトのシステムに 対応したビューを作ります 線画が単なるビューであるならば SwiftUI上のものは全て 線画に適用することができます レイアウトや アニメーションの修飾子は 全てビューと同じように 線画にも適用することができます グラフィックスのためだけの 修飾子も多数追加しました ぼかしや影などです 線画もビューなので これらは 通常のビューにも適用できます ビューとグラフィックスの 制御の統一は 今後 非常に有効になると思います もう少し見てみましょう 基本のパターンは図形と 色などのスタイルで これらの組み合わせがビューになります 先ほどのように 赤で塗りつぶして 赤い円を作ることもできます カプセル型を赤で描くなど 図形や操作を変えることもできます この場合は輪郭を塗りつぶしています
通常とは少し異なる線画が 欲しいこともあります 輪郭に沿ってではなく 形作るように描くことも バリアントの1つです 線画操作は1つ目の例のように 線幅を埋めるものか 他のグラフィックスAPIに 見られる― ダッシュ記号などの 線画の標準パラメータのいずれかです
以上が形と塗りつぶしですが 使ったのは色だけです 図形を埋めるものは他にもあります 画像を並べてみたり 様々なグラデーションを つけることもできます これはグラデーションの例です グラデーションには この基本型を使います 一次元のカラーランプが示されます ここでは7つの色を連続して 均等に配置したランプです 次はグラデーションの形を 選択します ここでは角度のついたものを使い カラーランプを追加し 中心点と開始角度を設定しました 中心点を基準に塗りつぶし 無限にグラデーションが続きます 色で塗りつぶしたものができました これを円に適用することもできます 最初の赤い円とは異なる カラーホイールができました 塗りつぶしは全体だけでなく リング状に行うこともできます
(拍手) 基本的なものを使って 個別の線画を見てきましたが 次は複数の線画操作やビューで もっと複雑なものを作りましょう この例を使って説明します サンプルは ダウンロードもできます これは双方向の 円グラフのようなもので 追加や削除が可能な カラーのくさび形で構成されています 描き方を理解する前に まずデータを見てみましょう これはデータモデルですが 非常に単純なもので くさび形のものの1つを表しています それぞれのくさび形には 形状と色の特性があります IDで追跡できる Dictionaryもあります また 描く順序を示す IDの配列もあります 次は描き方を見ていきましょう レイアウトのシステムと 連動させたいので 制御全体において レイアウトには限界があるとします 期待どおりのサイズ変更や移動を するためです これが機能することによって 色付きのものを 別々に描くことができます それぞれが同じ限界に収まることで 統合したときに整合するのです こういうものが出来上がります これは ある形をグラデーションで 塗りつぶしたものです このような図形です SwiftUIのツールキットには 組み込まれていない図形ですが カスタム図形を定義すれば 大丈夫です カスタム図形は カスタムビューに似ていて プロトコルに準拠するものです ここではビューではなく 図形のプロトコルに準拠します このプロトコルの要件は パスと転送先の関数だけです ここでの転送先は レイアウトの限界または基準枠で パスが返されます この図形には くさび形という 形状の特性だけを指示します 後から入力して返すための 空のパスも作成します もう少し分かりやすくするために 描く図形の形状を効果的に抽出すれば 三角比が非表示になります 円のような形状なのでそうしました 要するに この関数で使う変数を 定義するということです 最初に内側の弧を追加します 次に内側と外側の弧をつなぐ 線を追加して 円に沿って戻る弧を追加します 最後に始点と終点がつながる 現在のサブパスを閉じます 完成です 失礼 次はこれをグラデーションで 塗りつぶしましょう その前にもう1つだけ 必要な作業があります 図形のアニメーション化です 図形をこのまま使っても SwiftUIでは動かせません タイプの情報が不足しているからです そこで図形にanimatableDataという 特性を追加します これによってシステムが補間できる 浮動小数点数のベクトルが提供されます ここではこの特性を実装した データモデルに権限を委譲します つまり3つの特性と くさび形の形状を 補間できる値に統合するだけです
図形が完成したので次は描画です 図のまとめ方を思い出してください 角度のついたグラデーションで 図形を塗りつぶします これを8つ作成して まとめたいと思います 使うのはZStackというもので
HStackやVStackに似ていますが 空間に対して深さ方向に 層状にまとめるものです まず 環境からデータモデルを入手する カスタムビューを作ります 次にZStackを作成します データモデルと同じように wedgeIDsの配列があります この配列をForEachビューで使います 何をするのかと言うと IDの配列をマッピングし くさび形ごとにビューを作成します このビューは非常に単純で 1つのステートメントで グラデーション付きの図形を作成します これで ほぼ完成です SwiftUIは依存関係を追跡するので データモデルとビューは同期します ZStackが挿入と削除を処理するので フェードインとフェードアウトも 手際よく行われます もう少し追加します 動画にもあったように くさび形をタップして削除しましょう tapActionを追加します これはビューのイベントハンドラで ビューの図形をタップすると それを閉じるというものです 動画を有効にし データモデルに wedgeIDの削除を指示します これでタップが可能になります 最後にもう1つ追加します デフォルトの遷移は フェードインとフェードアウトです もう少し面白くするために 同時にくさび形を 中心に向けて縮小しましょう transitionに scaleAndFadeを追加します これは自分で行う必要があります この遷移の仕組みを見てみましょう ビューを削除すると くさび形が縮小してフェードアウトし 挿入すると くさび形が拡大してフェードインします 図形はビュー階層にあって 遷移を反転させているだけです アニメーションシステムがあるので 細かいフレームは不要です 最終状態を定義すれば 残りはこのシステムが処理します この例は対称的な遷移なので 最終状態は2つだけです 遷移が進行して削除される状態と 何も進行していない通常の状態です
これらの状態はコードで定義します SwiftUIではビューの修飾子で行います ビューの修飾子は 他のビューの機能として ビュー階層の一部を定義します これが修飾子の示していることです ビューの修飾子には方式があります この場合の方式は別のビューの機能で 内容パラメータのことです これを別のビューに適用して 変更を加えます この例では2つの遷移状態があります 遷移が有効かどうかの ブール型のプロパティを加えます 有効な場合は既存の 2つの修飾子を適用し 入ってくるビューが遷移を 有効にするようにします 削除する場合は図形を縮小して フェードアウトさせます
以上が遷移の説明ですが 最後にもう1段階あります 設定したビューの修飾子には 有効か無効かの値を 設定する必要があります 1つの遷移としてまとめるためです そして追加や削除の際に 正しい値を適時に選び アニメーション中に補間します このような機能を利用します 出来上がったものの デモを見てみましょう このアプリケーションは ダウンロードできます 実行して ウィンドウが開きました
まだデータモデルがなく空ですが ここに追加します 遷移が適用されて 拡大や塗りつぶしが行われています タップすると 項目を削除することができます 中間の項目を削除すると 定義に従って図形が回転します アニメーションを 起動することもできます 物理シミュレーションが 実行されています パラメータ空間を 不規則に移動しています
簡単なアプリケーションですが アニメーションの起動中も 双方向性が維持されます このグラフィックスのレンダリングの 面白いところは SwiftUIが画面上の 全ての項目1つずつに 固有のプラットフォームのビューを 作成することです ボタンのようなものです 主に通常の制御を扱う私たちが 求めているものです このようなグラフィック表示を UIKitやAppKitで描く場合は くさび形ごとに1つのNSViewを 作成しないでしょう くさび形が多い場合に 求める性能が得られないからです 私たちはNSViewを このようには使いません SwiftUIでは全てをZStackの中の drawingGroupに収めて この問題に対処します drawingGroupは 特別なレンダリングの方法で グラフィックだけに使うものです 方法としては全てのSwiftUIのビューを NSViewまたはUIViewに平坦化し Metalでレンダリングします 動作の変更ではないので 動きは全く変わりません 項目が増えた場合の動きは 先ほどの例よりかなり良いと思います (拍手) ビューが1つで システムとして理想的だからです ハードウェアアクセラレーションを 1度しか使わないのです
こんなところでしょうか? 以上です
(拍手) グラフィックの修飾子を いくつか紹介しましたが 実際にはもっとたくさんあります これまで通常の2D描画システムに 注力してきましたが ビューについても同じことが言えます 今日の詳細については 資料を参照してください
私たちが作ったモデルの力を 理解していただけると思います 私たちはグラフィックに関する これらのものを レイアウトなどのAPIと 連携して利用したいと思っています SwiftUIはこれらの領域全てを 1つのプロトコルに統合し 全てを同じパッケージにまとめます 皆さんが今後利用されることを 楽しみにしています ありがとうございました (拍手) 1時間後にもう1度ラボがあるので 何か質問のある方はいらしてください ありがとうございました (拍手)
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。