ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Mac Catalyst Appのインターフェイスの最適化
Xcodeの新しい「Optimize Interface for Mac」オプションを使用することで、アピアランスも操作感もMacらしくなるようにMac Catalyst Appをカスタマイズする方法をご覧ください。Catalyst App向けの新しいレイアウトおよびアピアランスオプションを知り、それらによってグラフィック性能の向上、よりシャープなテキスト、そしてAppleのデスクトップとラップトップ専用にデザインされたインターフェイスを提供できることを学びましょう。ここでは、それらのオプションを利用して複数のプラットフォームを開発する際に、コードを体系化するベストプラクティスをご紹介します。Mac Catalystプロジェクトに積極的に取り組んでいるデベロッパは、このセッションを最大限に活用できるでしょう。Catalystを扱うのが初めての方は、まず"Designing iPad Apps for Mac"と"Introducing iPad Apps for Mac"をご覧になることをお勧めします。Mac Catalystでの作業の詳細については、"What's new in Mac Catalyst"をご覧ください。
リソース
- Adding Menus and Shortcuts to the Menu Bar and User Interface
- Human Interface Guidelines: Mac Catalyst
- Mac Catalyst
- Optimizing your iPad app for Mac
関連ビデオ
WWDC21
WWDC20
WWDC19
-
ダウンロード
こんにちは WWDCへようこそ
“Mac Catalyst Appの インターフェイスの最適化” Cocoaエンジニアのニック・タイスラーです この後 同僚のジェイク・カーターも加わります
今回 皆様にご紹介するのは Mac Catalyst Appの インターフェイスを最適化する方法です
Mac Catalyst Appの次なるレベルを “Mac向けの最適化”と呼んでいます 最適化することで Macライクな ビジュアルとコントロールを iPadに合わせてスケーリングされた Catalyst Appに導入します
Mac向けの最適化は ビルド設定の変更で行い 起動時に適用されます Macに最適化されたAppは Mac向けイディオムで起動し iPadに合わせてスケーリングされた Catalyst Appは iPad用イディオムで起動します
この設定自体は コンパイルには影響しませんので ビルドエラーやフレームワークの問題が 発生することなく iPadに合わせてスケーリングされた Catalyst Appを最適化できます お勧めしたいのは Catalyst Appで あらかじめ作業をしてから Macに移行することです Macに最適化する前に やることがたくさんあります メニューバーアクションの キーボードショートカットを追加したり UIContextMenuInteraction APIで 右クリックの処理を追加するなどです
WWDC 2019のセッションで Catalyst Appに追加できる 機能が紹介されています これらは Mac向けの最適化の 前に実施します それでは 実際の“Mac向けの最適化”を 見ていきましょう 具体的に 何を最適化するのでしょうか? “何が最適化されるか?” Mac向けの最適化とは 要は ビジュアルをMacライクにすることです iPad Appを 最適化された Catalyst Appにする場合 コンテンツを1:1でレンダリングします “コンテンツスケール” Catalyst Appを iPadのビューに合わせる時の 77%ダウンスケールと 比較してみましょう コードでの100ポイントは 画面では77ポイントになります
Mac向けに最適化する場合 コードでの100ポイントは 常に 画面での100ポイントになります コンテンツのスケーリング以外にも App全体で多くの ビジュアルが改善されています
“macOS用システムコントロール” UIButtons UISlider UISwitch などのコントロールや― UIActivityIndicatorを 使っている場合 macOS用システムコントロールとして レンダリングされます こちらは iOS用システムボタンです 最適化すると Mac向けシステムボタンとして レンダリングされます
最適化されたコントロール表示の サイズとメトリクスは Macのコントロールに対応します iPadの表示と同じサイズには ならないかもしれません レイアウトを少し 変更することになります “macOS用フォントメトリクス” CatalystをMacに最適化すると テキストスタイルとサイズ― たとえば 本文 タイトル コールアウトが Macに合わせて調整されます iPadに合わせてスケーリングされた Catalyst Appと違い テキストはスケーリングされません テキストスタイルとサイズは異なります
たとえば 本文テキストのスタイルは iPadに合わせてスケーリングされた Catalyst Appでは17ポイント Mac向けに最適化された Catalyst App では13ポイントを返します 元のフォントサイズの方が スケーリングされた フォントサイズよりもずっとシャープに見えます “ハードコードされたサイズは調整されない” また ハードコードされた フォントサイズは 調整されません 従って― UILabelやUITextFieldでは 残念な結果になるかもしれません 一部ですが およそ23%は Macではしっくりこないでしょう
“macOS標準スペーシング” 最後に 知っておくべき 明らかな変化は 自動レイアウトがMac向けスペーシングを 使用することです ビルドの制約条件でシステムの スペーシングを使用する場合です システムのスペーシングは通常 Macの方がiOSよりも大きいです “何が最適化されるのか?” これらは上級機能ですが Mac Catalyst Appを Mac向けに最適化する際に影響します
CatalystのMac向け最適化について 考えてみましょう AppをMacに移行する場合の オプションは増えています CatalystをMac向けに最適化するか CatalystをiPadに合わせてスケーリングするか どちらかを選ぶことになります 両方はできません 2つを直接比較してみましょう “優れたMac向けApp” まず どちらかが 使えなくなることはありません iPadに合わせてスケーリングするほうが Appには好都合かも知れません 引き続き優れたAppを 両方のイディオムで利用できます “Mac向けAppへの早道” スケーリングはMac向けAppへの 早道です Macで実行できるようになります ビューを書き直すことなく 優れたMacバージョンを作成できます これは スケーリングの 理念のひとつでもあります Mac向けAppをすぐに実行するには 少々トレードオフがあります “スケーリングされた Appの 次のステップの可能性” トレードオフには 好ましいものと そうでないものがあり Appによって異なります Mac向けの最適化が 次のステップになるかもしれません “iPadとの互換性を備える” スケーリングされたAppは iPad Appとの互換性を備えます Catalystができることは 全てしてくれるため Appのコードを全面的に 変更する必要はありません それでも Macに合った アピアランスを実現できます “本格的なMac向けルックアンドフィール” 最適化されたAppは 本格的なMac向けルックアンドフィールを備えます CatalystがMacのコントロールに変換し テキストサイズは保持され Appのインターフェイスは Macでベストの表示になります “レイアウトを保持” スケーリングされたAppは AppのiPadバージョンと 互換性があるため レイアウトは可能な限り 保持されます ほとんどの場合 ビューを変更する必要はありません そのまま Macで適切に レイアウトされます “レイアウト作業の必要性” しかし 最適化ではコントロールの 見た目が完全に異なるため ある程度のレイアウト作業が 必要になるでしょう コントロールのサイズが暗黙的に 仮定されている場合がそうです 最適化とイディオムの差異について 理解できたところで どのようなAppで 最もメリットが 大きいか考えてみましょう どのようなAppを最適化すると アピアランスがよく表示されるのでしょうか? “候補となるAppの特徴は?” 表示テキストが多いAppでは ビジュアルがはっきりと向上します 元のテキストサイズが得られ テキストがスケーリングされないためです “テキストが多い” Swift Playgroundsは 今年発表のBig Surで― Mac向けに最適化される Catalyst Appです Swift Playgroundsのリッチテキストエディタで Appのビジュアルが向上しています 手間をかけるだけの 価値はあります “グラフィック重視” グラフィック重視のAppも 向上する可能性があります “SceneKitまたはMetal” MetalまたはSceneKitによる グラフィックを多用したAppでは 大幅な向上が見られました Swift PlaygroundsはLearn to Code Seriesで SceneKitを使用しています Mac向けに最適化したことで フレームレートの向上と電力消費の低下が 統合 ディスクリート― どちらのグラフィックシステムを 搭載したMacでも見られました “カスタムアートワーク” グラフィック重視のもうひとつのタイプが 高カスタム性の 細密なアートワークです Catalystのスケーリングと最適化の相違は 表示した時の視覚的な差異― すなわち pngをあるサイズに スケーリングしたものと 元のサイズの違いと同じことです Appを並べてみれば 見た目の違いを簡単に評価できます MapKitのアートワークは これを示すよい例です MapKitには 膨大なカスタムデザインと アイコンがあります 拡大されたバージョンで 場所マークのピクセルパーフェクトの アイコンを見てみます スケーリングありなしの 差が分かります
左はスケーリングされた― “カリフォルニア科学アカデミー”の アイコンです 右はスケーリングなしの画像です 建物の窓や柱を数えられるでしょうか こうした細部は スケーリングされた グラフィックでは失われます
道を下ると“デ・ヤング美術館”があります ここでも スケーリングなしは テキストもシャープです
最後は“グッゲンハイム美術館”です スケーリングなしのMapKitアセットでは 建築物の丸みが より完全に表現されています
アイコンは画面ではずっと 小さくレンダリングされますので ユーザーから見た 視覚的な差異は ここで見るほどは 目立ちません “パリ”
とはいえ これはMapKit全般に 言えることです パリのような密集した都会を 低い拡大率で 道路網まで細部に表示することは Catalyst Appを最適化する 強い動機になります MapKitでは顕著です “コントロールビュー” 候補となるAppの もうひとつの特徴は コントロールビューが多いことです 可変値を表示する Popoverなどです この場合 Mac向けに最適化すると ビューの表示がよくなるでしょう 地図ではこのメリットが大きいです このようなチェックボックスは スライドスイッチよりも 表示が向上します 最後に サードパーティへの依存状況が 制限要因になるかもしれません フレームワークデベロッパの 皆様にとっては Catalystのターゲットのサポートだけでは 十分でないかもしれません 特に お使いのフレームワークが UI中心の場合 Mac向けの最適化を サポートすることは クライアントを限定しない ために必要になるでしょう ジェイクが後ほど 皆様に コードを変更して Mac向けイディオムをサポートする方法を ご紹介します “iPadに合わせてスケーリング” ここからは Mac向けの最適化によって iPadに合わせてスケーリングされた Appのビューの状態を見てみます 左は タルトのレシピの最初に出てくる シンプルなビューです スタックビューでできたレイアウトに 5つの要素― ボタン ラベル スライダー 画像 テキストビューがあります
レイアウトを調整せずにMac向けに 最適化します 詳しく見てみましょう
“Mac向けに最適化” このレイアウトでよさそうです
最初に目に入る違いは 左側のシステムiOSボタンが 右側のシステムMacボタンに変わる 点でしょう ボタンは 実行時のサイズが変わり ビューのレイアウトによっては 原点も変わります
ウィンドウを見てください Macの画面上で まったく同一の ポイントサイズです
ただし― 左のウィンドウのポイントサイズと 全てのコンテンツは 実際には コード上のサイズの 77%だということです
これはCatalinaで 最初に Catalystがリリースされた時の動作です このことを この後の変更でも 頭に入れておきましょう
次に それぞれのウィンドウの リーディングエッジを見てみます ウィンドウのリーディングエッジと テキストビューの間のスペースは 右よりも左の方が はっきりと小さいです 前に見たとおり システムのスペーシング値が Macでは全般的に大きいのです
従って― Mac向けに最適化されたビューでは 水平方向の余白が広くなります レイアウトした時に ビューの幅が少なくなります このため テキストが次の行に 折り返して表示されています また スライダーの幅も 狭くなっています
しかし 幅が減少するにも関わらず 画像ビューは Mac向けに最適化したほうが 多くのスペースを占めています これは 必要な幅と高さの 制約に従い 組み込みコンテンツサイズに 合うようレイアウトされるのです
これで Mac向けに最適化すると 幅が広がる理由がお分かりでしょう この画像は元のサイズの 77%から 100%になったのです
状況に応じて これに対処する方法が いくつかあります MacまたはiPad固有のアセットを アセットカタログで表示する方法が最適です
適切にサイズ調整されたアセットがなければ Interface Builderの Trait Variationを使用して イディオムごとに別のサイズを 割り当てます これでスライダーのアピアランスに 対処できます スライダーが余ったスペースを 占めるよう構成します 余白と画像の幅が広くなり スライダーのスペースが狭くなりました
実は Mac向けに最適化された スライダーは 余白と画像幅の増分を 合計した量だけ狭くなります
レイアウトはこれでよいでしょう アピアランスを見ていきましょう
左側のスライダーは システムのUISliderです カスタマイズはしていません
右側では― 新しくデザインされたmacOS用スライダーの アピアランスが適用されています
トラックの高さやノブの影が わずかに違います 皆様とユーザーにとって 重要になるのは このスライダーが 他のMacのスライダーと 同じであることです このスクリーンショットでは はっきりとは テキストのレンダリングの 違いが分かりません このテキストビューは 本文テキストスタイルです 左側は iPad標準の17ポイントの フォントサイズです 右側は Mac標準の13ポイントです 視覚的に フォントのサイズは 揃っています しかし Mac向けに最適化されたビューでは 元の13ポイントになるため テキストはよりシャープです 折り返し表示で行が増えるのは 水平方向の幅が減少したためで テキストのレンダリングやサイズとは 無関係です 具体的な数字を知りたい場合は このスライドにレイアウト値が ありますので これまでの変化と合わせて 確認してみてください
これら全てを総合すると ご自身で ある程度の作業を行って Mac向けに最適化すれば シンプルな レイアウトもリファクタリングできるのです
このMac向けレイアウトを整理しましょう
AppはMacに合っていますので レイアウトに一般則を適用することよりも どの要素がどう変わるかを 理解するために見てきました 完成したAppがこちらです 完璧に なじんでいます
このビューを終える前に 左側の“Get Cooking”ボタンを見てみましょう ティントカラーがsystemTealに セットされていますが 右側では― Macのアピアランスに まったく反映されていません ティントカラーのボタンはMac標準ではなく ユーザーには違和感があるため Macの最適なアピアランスからは 外されています 実は コントロールのカスタマイズには さらに大きなカテゴリがあり Mac向けに最適化すると 使用できません iPadでは一般的なカスタマイズでも Macではそうでないか あるいは Macのビジュアルでサポートされて いないためです “UIControlのジェスチャー認識” よく ジェスチャー認識をUIButtonに割り当てて 長押しを検出し 複数のアクションを ボタンにオーバーロードしますが UIButtonのジェスチャー認識は Macのアピアランスに合わせたシステムボタンでは 呼び出されません
これに対処する方法はいくつかあります まず iPadの操作をMacに移行しようと していないことを確認します Appのメニューバーの メニュー項目か UIButtonのメニュープロパティを使って メニューを割り当てる方がうまくいきます マップがその例です Appの最適化では カスタマイズされたコントロールを― イベント処理レベルで 監査することをお勧めします これは UIButtonなどのコントロールの ジェスチャー認識や UIControlのイベント追跡メソッドの オーバーライド たとえば― beginTrackingTouchWithEvent などを含めてです ジェスチャー認識が 不可欠な場合は カスタムボタンタイプはMacのアピアランスで レンダリングされず かつ iPadと同じイベント追跡が維持され ジェスチャー認識の機能は 失われないことを覚えておいてください カスタマイズされた スライダーバーを見てみます “UIControlの視覚的なカスタマイズ” ただいま離陸しました minimumTrackTintColorは白 maximumTrackTintColorは青 円形のつまみを飛行機に 変更しています
これをMacに移行すると 乱気流に遭遇するかもしれません Mac向けイディオムでは これらのカスタマイズを利用できません 考え方としては システムコントロールを使用すると アピアランスのカスタマイズに 制限があり Mac標準のアピアランスでは UIKit APIを重ねられません
それでは ジェイクに交代して スケーリングされた― Catalyst AppをMacに 最適化する際の変更点を紹介してもらいます
Xcodeチームのエンジニアの ジェイクです シンプルなMac Catalystの 料理本Appを ご紹介します 早速デモをお見せしましょう
まず このAppの クイックツアーを行い Mac向けに最適化します 左のスライダーバーで エリアに簡単にアクセスできます “全てのレシピとお気に入り” などです 次に レシピのリスト 最後がレシピです
新しいレシピは “New Recipes”ボタンで追加します
レシピの詳細と写真を追加できます このAppは Macでも問題ありませんが 場違いに感じる点が いくつかあります iOS風のボタンや ナビゲーションバーです Xcodeに切り替えて 始めましょう
ターゲットの全般的な設定です Mac向けチェックボックスの隣に 新しいポップアップボタンがあります
“iPadに合わせてインターフェイスを スケーリング”がデフォルトでは 選択されます Xcode 12とmacOS 10.16で 新たに “Mac向けにインターフェイスを最適化”の オプションが加わります これを選びます
これは UIKitに対し Appを Mac向けイディオムで実行したいと伝えます Macライクなルックアンドフィールに なります Build & Runをして どう変わるか見てみましょう
“iPadに合わせて インターフェイスをスケーリング”と “Mac向けにインターフェイスを最適化”の 両方を同時に見られるようにしてあります まず ウィンドウが大きいです 加えて ウィンドウの中の コンテンツも大きいです スケーリングされなく なったためです
次に この“タイマー”ボタンです “Mac向けにインターフェイスを最適化”に 変更することで このボタンが自動的に Macライクなアピアランスになっています
他にも変化があります スケーリングされなくなって ボタン表示が大きくなったのに加え 異なるメトリクスが使用されています つまり どういうことでしょう?
一部のサイズは実際に変更されています たとえば フォントは大きくなり テキストとボタンの間の 余白も増えました
このように変わったせいで ボタンのフレームが大きくなっています このようなメトリクスの 変更を考慮して 変更後のレイアウトを 監査する必要があります
レシピのリストを 詳しく見てみましょう 左の画像と 右の“お気に入り”アイコンが スケーリングされなくなり やや大きくなっています ただし よく見ると テキストのサイズは変わっていません 動的テキストスタイルを 使用しているため 自動的に読みやすく 調整されています テキストのサイズはよさそうです リストの見た目は― 画像と“お気に入り”アイコンが 元のように もう少し小さい とよいのですが Xcodeに戻って リストを改善しましょう
レシピのリストを制御する View Controllerです ここでレイアウトを生成しています 高さは絶対値指定で 100ポイントです
自動レイアウトの制約を 設定した方法により 高さが小さいと レシピ画像も 小さくなります
先ほど調整してみたところ 80ポイントが ちょうどよさそうでした
この イディオムをチェックする行を 更新します Macなら 高さを80ポイントに そうでなければ 100ポイントのままにします 今度は “お気に入り”アイコンです
アセットカタログで 画像を指定してあります “Universal”Versionとだけ しておいて― どこでもAppを 実行できるようにしています この場合 iPhone iPad Macです テキストの隣に表示される画像が Macで実行した時に 大きすぎました
Mac向けイディオムで 実行したいので― アセットカタログで定義される Mac向けアセットにアクセスします 画像を特別に 手作業で 正しいサイズにして使えます
Mac向けアセットを有効にするには インスペクタの“Device”セクションの “Mac向け”チェックボックスを クリックするだけです アイコンのMacバージョンをドラッグして―
できました ビルドと実行で またリストを見てみましょう セルの高さが小さくなり レシピ画像も 小さくなりました 右の“お気に入り”アイコンも テキストに合ったサイズです Mac向けの特別なアセットを 使用したためです
“新しいレシピ”画面を 見ていきましょう
“写真を選ぶ”ボタンが Mac風になりました いいですね それでも Macでは場違いに 感じるのが― 上のナビゲーションバーです ナビゲーションView Controllerと ナビゲーションバーは Child View Controller間の 切り替えに便利です 小さい画面に多くのデータを 表示できます このような操作は Macでは違和感があります よく使うボタンをナビゲーションバーに 配置するのも一般的ですが Macの場合 ウィンドウタイトルを 表示するツールバーが よりよい選択です そこで “保存”と“キャンセル”ボタンは 移動して 上のツールバーか 下のビューに ナビゲーションバーは なくしてしまいます “保存”と“キャンセル”ボタンは このビューで入力された データに対するアクションで また 両方ともウィンドウを 閉じるので このビューの下に移動すると よりMacライクになります また バグがあります Mac向けイディオムで実行すると タイトルラベルが他の2つの ラベルよりも大きいです Xcodeに戻って 修正してしまいましょう
“新しいレシピ”ウィンドウで使う RecipeEditorViewControllerです Macでの実行時に ナビゲーションバーを非表示にします
これを修正するには イディオムをチェックします Macなら ナビゲーションバーを非表示に シンプルですね 次に ストーリーボードを 見てみます
このプロジェクトは Mac向けイディオムをサポートするので 下のデバイスバーに イディオムの選択が表示されます
Macに切り替えるので 上のビューを見てください
キャンバスが更新されています コントロールがずっと Macライクになりました Mac標準のスペーシングと メトリクスが使われています
ここでまず修正したいのが タイトルラベルです 他の2つよりも 大きく表示されていました
インスペクタの “Font”属性を見ると 17ポイントのシステムフォントに 設定されています ニックの説明にもあったとおり これは およそ13ポイントに スケールダウンされていました “Mac向けにインターフェイスを最適化” オプションを選択したため スケーリング前の 17ポイントでレンダリングされます これが 大きく表示された理由です では 他の2つはなぜそのまま?
他の2つのラベルは どちらも 本文テキストスタイルに 設定されています 先ほど説明したように テキストスタイルは動的で レンダリングされるプラットフォームに 適切なサイズに調整されるのです なるべく このようなテキストスタイルを 使うことをお勧めします タイトルラベルを更新して これも“本文”テキストスタイルに しましょう
次は“保存”と“キャンセル”ボタンの 処理です コード上で 既にナビゲーションバーを 非表示にしましたので ここで見えていても 実行時には表示されません 新しい“保存”と“キャンセル”ボタンを このビューの下に追加して Mac向けイディオムでのみ 表示されるよう設定します
まず ボタンテキストビューをリサイズして 新しいボタンの場所を作ります
この時 Interface Builderで キャンバス内のフレームが 自動レイアウトで 計算されるフレームと一致しないと 警告されます とりあえず ボタンの場所を作ります
スタックビューをドラッグして―
右下にピン留めします
スペーシングを システム標準のスペーシングに―
さらに“配分”を“等分”にセットして ボタンのサイズが 揃うようにします
次に ボタンをドラッグして タイトルを更新します
“キャンセル”“保存” これらのボタンは Mac向けイディオムの時だけ 表示したいので スタックビューを選択して―
“Installed”属性を使います 属性インスペクタの 下の方です
ビューについては この属性は 実行時にビュー階層に 追加されるかどうかを指定します “Installed”をチェックすると このスタックビューは常に ビュー階層に追加されます ただし Trait Variationを 左下の+ボタンをクリックして 追加できます
“イディオムに基づくバリエーション導入 幅 高さ 色域” トレイトに基づき 属性を変えられます Xcode 12では新たに Mac向けイディオムに基づき トレイトを変えられるようになりました これらのトレイトに基づき バリエーションを追加します
新しいMac向けイディオムのバリエーションでは “Installed”をチェックしたままに― “デフォルト”ケースではチェックを外します
これで これらのビューは Mac向けイディオムの時だけ設定されます
次に テキストビューが 新しいボタンに重なっていないか確認します
Controlキーを押しながら スタックビューにドラッグします
“垂直方向のスペーシング 垂直方向のベースライン標準スペーシング” キーボードでOptionキーを 押し続けると 上の選択内容が 垂直方向のスペーシングから 垂直方向の標準スペーシングに変わります こちらを選びます
制約が追加されましたが 他の制約が赤色に なってしまいました 条件を満たさない 制約があるという意味です 下の赤色の制約は テキストビューと スーパービューの 間のスペーシングが 20ptのままです これが 先ほど追加した 制約と競合しています
古い制約を削除するだけでは iPadで実行した時に 新しいボタンが設定されません テキストビューの高さを 別の方法で指定する必要があります 方法はいくつかあります ここでは 制約の優先度を 変更します
現在は“Required”に セットされていますが “High Priority”に 変更してみましょう 制約を満たせない場合は 無視されます
全ての制約が オレンジ色に戻りました “Update Frames”ボタンを クリックします 右下にあります
これで MacとiPad 両方のイディオムで機能します もう一度 デバイスバーの イディオム選択で確認します
“新しいレシピ” “新しいレシピ”ウィンドウを 表示すると タイトルラベルのサイズは適切に ナビゲーションバーは非表示に “保存”と“キャンセル”ボタンは 下に配置されています よりMacライクになりました スライダーバーに戻って まとめてみましょう “まとめ” “Mac向けにインターフェイスを最適化” オプションを選んで Mac向けイディオムで実行したいと UIKitに伝えます ルックアンドフィールが よりMacライクになります ただし レイアウトが 変わってしまいます Mac標準のスペーシングと コントロールのメトリクスが適用されるためです コントロール 画像ビュー フォントサイズが変更されます 変更された後のレイアウトを 監査する必要があります ログをチェックして 条件を満たさない制約の 警告を調べるとよい場合があります コントロールのサイズが変更されたために 起きた問題を探すのに役立ちます 先ほど見たように アセットカタログの Mac向けアセットでも 解決できる場合があります
ここで 考慮すべきことは Mac向けアセットが見つからないと システムは Mac向けスケーリングと iPad用アセットにフォールバックします “Universal”アセットはその後です デモで見たように これらのアセットのサイズは 最適でないかもしれません 画像を監査して なるべく Macバージョンを表示します 最後に MacとiPadの パラダイムの違いを見てきました ナビゲーションバーや ボタンの配置などです このような差異を考慮して Mac Catalyst Appを デザインします
詳しくは Appleのヒューマン インターフェイスガイドラインで Macですばらしく見せる デザインの洞察を得られます
Mac向けに最適化された Mac Catalyst Appを バックデプロイすることも 可能ですが 考慮すべき点が いくつかあります “Mac向けに最適化された インターフェイス” Catalinaでの実行時に Macライクなルックアンドフィールに なりません また iPadとMacの両方のイディオムを サポートすることを意味します バックデプロイ時の 作業の大半は レイアウトが 両方のイディオムの メトリクスに対処することの確認です また Macでサポートされない フレームワークやAPIを 使わないようにします
traitCollectionで イディオムをチェックするか―
条件付きコンパイルブロックを 検討してください この条件付きコンパイルブロックは イディオムに関わらず同じです
両方のテクニックを使うことが 適切な場合があります
私からは以上です 再びニックから SwiftUIの最適化を 説明してもらいましょう ありがとう ジェイク 料理本 Appが Macでうまく表示されるようになりました
SwiftUIで Catalyst Appを iPadに合わせてスケーリングできますが “Mac向けに最適化されたSwiftUI” SwiftUIは Mac向けの最適化にも使用できます SwiftUIはUIKitと似た アプローチを取りますが 得られるものはより多いです 宣言型シンタックスとレイアウトのおかげで 趣向を凝らしたビューを Mac向けに最適化できます
SwiftUIをお使いの方は コンテキストアウェアな方法で ビューが調整されることをご存じでしょう たとえば フォームやメニューなら ボタンスタイルを変えるなどです SwiftUIは ビューがMac向けイディオムで 使われているかも把握しています コントロールのアピアランスが iPadOSビューで変わるように Mac向けに最適化された時も 異なる表示がなされます
やはり 少々のレイアウト作業を行い SwiftUIインターフェイスを Mac向けに最適化します SwiftUIのレイアウトは 全体的に ビューの構成に使用するコンテナに 基づき変わります いつもどおり 柔軟性のある 再利用可能なレイアウトの構築は ビューのモジュール化と コンテナの適切な使用により可能です
では駆け足で SwiftUIコントロールの アピアランスの変化を見ていきましょう iPadに合わせたスケーリングから Mac向けの最適化への変化です 今年 GroupBoxが iOSとiPadOSに新たに加わります GroupBoxは 構造化コンテンツの レイヤー化に使用し 適切なセマンティックカラーを 自動的に受け取って コンテンツの背景とします “基本情報” “詳細の記述” ここでは GroupBoxを 別のGroupBoxに ネストされたVStackに使用します ビューは自動的に 異なる背景色にレンダリングされました SwiftUIのGroupBoxは Mac向けGroupBoxのように Macに最適化された アピアランスになります 同時に 自動余白と レイアウト調整が行われます
コードにToggleをセットしました これは Boolのバインディングに 変数completedを使用します iPadOSでSwiftUIの Toggleのデフォルト表現は スライドスイッチです
Mac CatalystとUIKitでは Mac向けチェックボックスを利用できました SwiftUIでも利用できます Macに最適化された Catalystでは デフォルトのToggleのアピアランスは チェックボックスです ToggleStyle構造体と ToggleStyleモディファイアを使って デフォルトが合わない場合 スタイルを特定できます
UIKitと同じく システムiOSボタンには ネイティブのMacのアピアランスが適用されます 横にSF Symbolsを付ける こともできます
どちらもすばらしい ボタンです 私の場合 一方が よりクリックしやすく感じます
DatePickerは少々違います 右側は Catalystの両方のイディオムの DatePickerです DefaultDatePickerStyleは Catalyst全体で同じようにレンダリングします ここでも もちろん 別のスタイルも利用できます DefaultDatePickerStyleは どちらのイディオムでもコンパクトです “サイズ” このコンテンツビューでは サイズを 小 中 大から選べます sizeIndexプロパティで バインディングを設定します stringの値またはenumで バインドすることもできます
Mac向けに最適化すると Mac向けピッカーを使用できます これは Macでユーザーにおなじみの コントロールです スクロール式のピッカーよりも ずっとMacライクな体験を提供します segmentedControlStyleも 両方のプラットフォームのオプションです
最後のコントロールです システムボタンを利用できますが SwiftUIボタンでは カスタマイズ性を 犠牲にすることはありません システムボタンのアピアランスを 使わない場合 カスタムボディを使います このコードのように SwiftUIの ButtonStyle APIで 柔軟で再利用可能な ボタンスタイルにしてみてください
SwiftUIとCatalystは カスタムボタンを Macのアピアランスでレンダリングしません カスタムボタンはそのまま 表示されます
ここでは 完全にカスタマイズした 懐かしの90年代風 ButtonStyleを書いてみました ビューのモディファイアとして ContentViewのボディにある ボタンに適用しています Catalystのおかげで iOSでもMacでも コードを追加しなくても 利用できます
私も皆様と同様 SwiftUIで 実験してきました SwiftUIはUIKit Appで シームレスに実行できます コンテンツビューを とても簡単に SwiftUIプレビューキャンバスで 構築できます そして UIKitのコードの UIHostingControllerに ドロップします
私の印象では― ジェイクの料理本Appの テキストのみのビューは 模様替えできると思います
インタラクティブなレシピビューを 全てSwiftUIで作成しました レシピを見ながら 調理の進捗をチェックできます
上のピッカーで 計量単位を選びます
各手順はGroupBoxに 含まれており―
Toggleで完了にマークできます
カスタムの青色のタイマービューと カスタムSwiftUIボタンを使用して タイマーを制御しています さて このiPad用の機能は Mac向けに最適化されたCatalyst App ではどうなるでしょうか こちらは プレーンテキストの 調理手順のビュー こちらが iPad SwiftUIのビュー コードはまったく変えていません うまく変更され 満足です
ピッカーは デフォルトのMacの アピアランスを適用しています iOSスタイルのGroupBoxは Macスタイルになっています GroupBoxを使ったので さらにMacライクなレイアウトにできました そうでなければ 自分で 書かなければなりませんでした
Mac向けGroupBoxは Mac向けメトリクスを使用します ビューの周囲がやや ゆとりがなく GroupBoxのラベルは 外に移動しています 調理手順の見た目にも 影響しています
スライドスイッチは 全てチェックボックスになり チェックボックスのラベルは ボックスの右側に配置されています また ラベルは クリックして チェックボックスをトグルでき スライドスイッチのラベルとは 動作が異なります
このカスタムビューはそのままで 正しくスケーリングされています
とても簡単に このAppに SwiftUIを導入できました レシピを簡単に よりインタラクティブに MacとiPadの両方で使えます 今日学習した内容を まとめましょう Catalystインターフェイスの最適化で 変わる点について 見てきました
Mac向けの最適化の候補になりそうな Appの特徴を確認しました XcodeでAppを最適化する方法は ジェイクが説明してくれました Interface BuilderのTrait variationと コードのイディオムチェックを使用します
最後に SwiftUIをMac向けに 最適化する方法を見てきました そして どれほど簡単に SwiftUIビューをUIKit Appに 統合できるかを確認しました
ご視聴 ありがとうございました WWDC 2020をお楽しみください
-
-
22:04 - Hide Navigation Bar in Mac Idiom
if traitCollection.userInterfaceIdiom == .mac { navigationController?.setNavigationBarHidden(true, animated: false) }
-
29:33 - Idiom vs conditional compilation block
// Idiom vs conditional compilation block if traitCollection.userInterfaceIdiom == .mac { // "Optimize Interface for Mac" specific code } #if targetEnvironment(macCatalyst) // Mac Catalyst specific code #endif if traitCollection.userInterfaceIdiom == .mac { // "Optimize Interface for Mac" specific code } else if traitCollection.userInterfaceIdiom == .pad { #if targetEnvironment(macCatalyst) // Mac Catalyst specific code #else // iPad specific code #endif }
-
31:26 - SwiftUI GroupBox
// Nested GroupBoxes struct ContentView: View { var body: some View { GroupBox { VStack { Text("High level information") GroupBox { Text("Some elaborate details") } } } } }
-
32:00 - SwiftUI Toggle
// DefaultToggleStyle struct ContentView: View { @State var completed: Bool = false var body: some View { Toggle("Complete?", isOn: $completed) } }
-
32:35 - SwiftUI Button
// System Button with SF Symbol struct ContentView: View { var body: some View { Button(action: { }, label: { HStack { Image(systemName: "rays") Text("Click Me!") } }) } }
-
32:56 - SwiftUI DatePicker
// DefaultDatePickerStyle struct ContentView: View { @State var dueDate = Date() var body: some View { DatePicker("Due:", selection: $dueDate) } }
-
33:14 - SwiftUI Picker
// DefaultPickerStyle struct ContentView: View { @State var sizeIndex = 2 var body: some View { Picker("Size:", selection: $sizeIndex) { Text("Small").tag(1) Text("Medium").tag(2) Text("Large").tag(3) } } }
-
33:55 - SwiftUI Nineties Style Button
// Custom gradient button struct CustomNinetiesButtonStyle: ButtonStyle { var angle: Angle = .degrees(54.95) func gradient(shifted: Bool) -> AngularGradient { let lightTeal = Color(#colorLiteral(red: 0.2785285413, green: 0.9299042821, blue: 0.9448828101, alpha: 1)) let yellow = Color(#colorLiteral(red: 0.9300076365, green: 0.8226149678, blue: 0.59575665, alpha: 1)) let pink = Color(#colorLiteral(red: 0.9437599778, green: 0.3392140865, blue: 0.8994731307, alpha: 1)) let purple = Color(#colorLiteral(red: 0.5234025717, green: 0.3247769475, blue: 0.9921132922, alpha: 1)) let softBlue = Color(#colorLiteral(red: 0.137432307, green: 0.5998355746, blue: 0.9898411632, alpha: 1)) let gradient = Gradient(stops: [.init(color:lightTeal, location: 0.2), .init(color: softBlue, location: 0.4), .init(color: purple, location: 0.6), .init(color: pink, location: 0.8), .init(color: yellow, location: 1.0)]) return AngularGradient(gradient: gradient, center: .init(x: 0.25, y: 0.55), angle: shifted ? angle : .zero) } func makeBody(configuration: ButtonStyleConfiguration) -> some View { let background = NinetiesBackground(isPressed: configuration.isPressed, pressedGradient: gradient(shifted: false), unpressedGradient: gradient(shifted: true)) return configuration.label .foregroundColor(configuration.isPressed ? Color.pink : Color.white) .modifier(background) } struct NinetiesBackground: ViewModifier { let isPressed: Bool let pressedGradient: AngularGradient let unpressedGradient: AngularGradient func body(content: Content) -> some View { let foreground = content .padding(.horizontal, 24) .padding(.vertical, 14) .foregroundColor(.white) return foreground .background(Capsule().fill(isPressed ? pressedGradient : unpressedGradient)) } } } struct ContentView: View { var body: some View { Button("Awesome", action: {}) .buttonStyle(CustomNinetiesButtonStyle()) } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。