View in English

  • メニューを開く メニューを閉じる
  • Apple Developer
検索
検索を終了
  • Apple Developer
  • ニュース
  • 見つける
  • デザイン
  • 開発
  • 配信
  • サポート
  • アカウント
次の内容に検索結果を絞り込む

クイックリンク

5 クイックリンク

ビデオ

メニューを開く メニューを閉じる
  • コレクション
  • トピック
  • すべてのビデオ
  • 利用方法

その他のビデオ

ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。

  • 概要
  • トランスクリプト
  • コード
  • SwiftUIの新機能

    SwiftUIを使ってすべてのAppleプラットフォームに対応するアプリを作成する方法を紹介します。SwiftUIの最新アップデートやvisionOS用の新しいシーンタイプについて詳しく学びましょう。最新のデータフローオプションでデータモデルを簡素化する方法やインスペクタービューについても確認します。またアニメーションを強化するAPI、パワフルなScrollView、整った表の作成、改善されたフォーカスやキーボード入力などについて紹介します。

    関連する章

    • 1:05 - SwiftUI in more places
    • 10:21 - Simplified data flow
    • 18:46 - Extraordinary animations
    • 27:18 - Enhanced interactions

    リソース

    • Playing haptics
      • HDビデオ
      • SDビデオ

    関連ビデオ

    WWDC23

    • アプリでシンボルをアニメーションする
    • フォーカスを使ったSwiftUIレシピ
    • 進化したScrollViewの詳細
    • Springsでアニメーション生成
    • Swift Chartsの円グラフとインタラクティブ性の詳細
    • Swiftの新機能
    • SwiftDataでアプリを構築
    • SwiftDataについて
    • SwiftUIとUIKitを使ったアクセシブルなアプリの作成
    • SwiftUIにおけるインスペクタの詳細
    • SwiftUIにおける空間コンピューティングの概要
    • SwiftUIにおけるObservationの説明
    • SwiftUIの高度なアニメーションの世界
    • SwiftUIアニメーションの詳細
    • SwiftUI向けのMapKitについて
    • SwiftUI向けのStoreKitについて
    • watchOS 10に向けたアプリのデザインおよび構築方法
    • Xcode PreviewsによるプログラマティックなUIの構築
  • ダウンロード

    ♪ ♪

    みなさん こんにちは SwiftUIチームのエンジニアのCurtです 私はJeffです 私もSwiftUIチームのエンジニアです SwiftUIの新機能を みなさんとシェアできて光栄です SwiftUIを使用できる場所が増えました 新しいプラットフォームもです! 新しいデータフロー型で ドメイン作りが簡略化され これまで以上のパワーを提供します インスペクタとテーブルの改良で データ表示が非常に向上します アニメーションAPIを改善し アプリの利用者に美しい体験を 提供することができます フレームワークを通して インタラクションをより向上させます パワフルなscroll viewの改良や Focusとキーボード入力の改善 ボタンやメニューなどの さらなるカスタマイズなどです まず SwiftUIをさらに新しい場所で 使用できるようになりました ヘッドセットやwatchOS 10 新ウィジェットや相互運用性の統合など SwiftUIはアプリの利用者が喜ぶ 素晴らしい体験作成のお役に立ちます 空間コンピューティングは SwiftUIに素晴らしい未来をもたらします 新しいVolumesの3D能力や イマーシブな空間での豊かな体験 3Dジェスチャーに効果やレイアウト RealityKitとの深い統合などがあります コアピースである コントロールセンターのHome Viewや 親しみのあるTVアプリ Safariや Freeformから Keynoteの新環境である Immersiveリハーサルなど SwiftUIがユーザ体験の中心にあります この新プラットフォームでは WindowGroupなど 慣れたシーン型でウインドウを作ります WindowGroupシーンは2Dでレンダーされ depth-sensitiveの 3Dコントロールが備わっています ウインドウ内で NavigationSplitViewやTabViewなど SwiftUIの通常のコンテナを選びます 他のプラットフォーム同様に このコンテナ内ではいつもの SwiftUIコントロールが使用できます さらに深さが必要なら シーンにvolumetric styleを適用します Volumesはボードゲームや 建築モデルのように3D体験を 限られた空間に表示します ほかのアプリの横に表示します メモにアイデアを書いたり Keynoteの編集中に あなたのコンテンツを使用できます Model3Dで静的モデルに ボリュームを与えます 照明効果などのある 動的でインタラクティブなモデルには 新しいRealityViewを使用します フル体験を求めるなら Immersive Spacesです Immersive Spaceシーン型は 環境はめ込み型であれ フルイマーシブであれ イマーシブで空間的な体験を定義できます システムはほかのアプリを全て隠し あなたが作り出した世界に ユーザーを没頭させます ImmersiveSpaceで 混合イマーションタイプとして アプリを実世界と結びつけ コンテンツをユーザーの環境と結合できます アプリの要素をテーブルや表面に設置し 仮想オブジェクトや効果などで 実世界を増大させ豊富にできます さらに完全没入してみましょう あなたのアプリがすべてを コントロールします これらの連結したイマーシブな体験を 大きなファイルサイズでも作動する Model3DやRealityViewで 作ることができます SwiftUIを使って 魔法のような体験を創造できます これらをさらに探求するなら 「Meet SwiftUI for spatial computing」を ご覧ください SwiftUIは部屋を満たす体験を つくりだせますが Appleの最もポータブルな ディスプレイの体験も創造できます watchOS 10はリデザインされた ユーザー体験をお届けします タイミングよく情報を表示し 焦点を当てたコンテンツを一見でき ディスプレイの形状と性能を フルに活用します この美しいフルスクリーンの カラーとイメージを利用し プラットフォーム全体で アプリをアップデートしました これらのデザインの根源は watchOS 10のためにパワーを得た 幾つかの既存のSwiftUI Viewsです NavigationSplitViewと NavigationStackには 美しいトランジションが加わります TabViewでは Digital Crownを活用した vertical pagingが加わります SwiftUIはこの色彩豊かさを Apple Watchアプリにも取り込むため 新しいAPIを用意しました containerBackground modifierで コンテンツを押し出すときにアニメーション化する バックグラウンドウォッシュを設定できます またwatchOSではTab viewsの バックグラウンドも設定できます そして新しいmultiplatform toolbar placementsである topBarLeadingとtopBarTrailingが 既存のbottomBarに加わり これらの小さいdetail viewsを アプリに配置できます これらに加え 幾つかの既存のAPIが初めて watchOSに加わります DatePickerと Listsのselectionがその例です これらの新しい機能で アプリを洗練しましょう まだApple Watchアプリがないなら 今こそ始めるべきです デザインと技術が一体になり これをどう実現したか 「Design and build apps for watchOS 10」でご覧いただけます そしてこれらのアイデアを 「Update your app for watchOS 10」で アプリに応用してみてください watchOS 10のSmart Stackウィジェットで アプリの情報を一見できるようになります どこに表示されようともウィジェットの コアはSwiftUIです。 このような新しい場所でもです iPadOS 17のロック画面のウィジェットは ホーム画面のウィジェットの 素晴らしい追加機能となります またStandby Modeで 大きく大胆なウィジェットが iPhoneの常時表示ディスプレイで 表示されます そしてmacOS Sonomaの デスクトップウィジェットは 日常の生活の中で最新情報を伝えます 新しい場所に加え 新しいテクニックも教えました ウィジェットに interactive controlが追加されます ウィジェットのトグルやボタンが App intentsを使ったapp bundleのコードを 作動できるです またウィジェットのアニメーション化が SwiftUI transitionと animation modifiersで可能になります これらの新機能を使うには 「Bring widgets to new places」と 「Bring widgets to life」をご覧ください 新機能を用いたウィジェットの 開発と洗練には Xcode Previewsが役に立ちます PreviewsはSwift 5.9でマクロを活用し 優雅な新しい構文を供給します Previewの宣言と設定や ウィジェット型の追加 テストのタイムラインを定義できます Xcode Previewsで ウィジェットの現状と 状態間の動画が見られる タイムラインを表示します もちろん新しいPreviewsは 通常のSwiftUI viewsと アプリも使用できます そしてXcode内で Macアプリのpreviews とインタラクトできます 詳細は 「Build programmatic UI with Xcode Previews」で アプリとウィジェットの開発を 加速させるため これらのツールをどう利用できるか 学んでください Previewsを可能にするマクロに加え Swift 5.9にはさらなる改良点があります Swiftの新機能の概要を 「What's new in Swift」で ご覧いただけます またSwiftUIは SwiftUI特定拡張子で ほかのAppleフレームワークに使用できます それらのフレームワークの中から 特に面白いものを いくつかご紹介しましょう MapKitではSwiftUIコード内で Appleのマッピングフレームワークを 活用できるようになりました SwiftUIとMapKitを取り込めばいいだけです ビューにマップを足したり カスタムマーカーやポリラインや ユーザーの現在地を足したり コントロールを設定したり出来ます SwiftUIに マップを足したいなら 「Meet MapKit for SwiftUI」を ご覧ください 2年目を迎えたSwift Chartsにも 改善点が加えられました それらにはScrolling chartsや Selectionの組み込みサポート そして多くのみなさんが要求していた 新しいSectorMarkを備えた ドーナツグラフと円グラフです これらの新機能については 「Explore pie charts and interactivity in Swift Charts」をご覧ください 顧客を集め維持するための 優れた体験をつくるのに役立つのが in-app purchaseと subscription storesです カスタムマーケティングコンテンツで subsrciption store viewを確認でき ブランドにマッチする フルブリードの背景 そして豊富なコントロール設定があります 「Meet StoreKit for SwiftUI」で in-app marketingに チャレンジしてみましょう 新しいプラットフォームやウィジェット 複数プラットフォームの統合 そしてwatchOSの美しさなどSwiftUIは Appleデベロッパ体験を追求し続けます SwiftUIを使える場所が増えて 嬉しいね Jeff もちろん! またすべてのプラットフォームで 利用できる改良点がたくさんあります その通り! それを使ってアプリを作らなきゃ そうだね! 僕のアイデアのこと考えた? 犬のアイデアかい? ああ バードウォッチングの犬版さ! そんなアプリ 必要かな? もちろんさ! 売り込み文句も必要ないよ 一攫千金のアイデアが浮べば アプリを作り始めるべきです 素晴らしいアプリには 素晴らしいデータモデルが必要です ではアプリのデータを扱うための SwiftUIの素晴らしい新機能を 見てみましょう SwiftUIのお気に入りの機能の一つは インターフェイスを アプリの状態の機能に定義することです model typeの定義の仕方で これまで最大のアップグレードがあります Observableマクロです Observable modelsは コードを簡潔で効率よくしながら データフローに 親しみあるSwiftUI patternsを 使用できます これは私が外出中に出会った犬のデータの 保存に設定したmodel classです これをObservableにするため マクロを足します それだけです ObservableObjectと違い propertiesをPublishedと する必要はありません Observable modelsはデータフローのために 既存のSwiftUI mechanismsに 簡単に統合できます DogCard viewを例に見てみます ViewでObservableを使用する時 SwiftUIは自動的にpropertiesに 依存を確立します またproperty wrapperを使う必要はなく view codeはスッキリします ここではisFavorite propertyを 読み込んでおり それが変わると再評価されます Invalidationは読まれたpropertiesだけに 起こりますので 不要なupdatesを起こすことなく modelをintermediate viewsに 通すことができます SwiftUIにはstateとそのviewsへの関係を 定義するツールがあり ObservableObjectと使用できるよう デザインされたものもあります Observableを使用すると それがさらに簡単になります State及びEnvironment dynamic propertiesと 直接働くようになっているからです read-only valuesに加え Observablesはmutual stateを 表すのに最適です Dog sightingフォームがいい例です State dynamic propertyを使って modelを定義し そのpropertiesのbindingsを propertyの編集を担う form elementにパスします 最後にObservable typesはenvironmentに シームレスに統合します アプリを通してviewsは 現在のユーザーをfetchしたいため root viewに environmentを足しました Environment dynamic propertyを使用し user profile viewが値を読みます ここではenvironment keyに typeを使っていますが custom keysもサポートします 「Discover Observation with SwiftUI」をご覧になり このパワフルな新ツールの利用方法を 学ぶことができます Observableで 明瞭で簡潔なコードが書けるため アプリ作成が始めやすくなります しかしお気に入りの犬を 見失わないためdata modelへの変更を 持続させたいとします SwiftDataはdata modelingと managementのための 新フレームワークです 迅速でスケーラブルで SwiftUIとうまく働きます SwiftData modelsはコードだけで表され どのSwiftUIアプリにも最適です Dog model typeを SwiftDataで設定するには ObservableからModel macroに切り替えます 変更はこれだけです SwiftDataが提供するpersistenceに加え ModelsはObservableの利点すべてを 受け取ります 非常にパワフルです ドッグウォッチングアプリの メイン画面には最近会った犬の scrolling stackが表示されます ここでSwiftDataを使うため 変更すべき点を見てみましょう まずアプリのdefinitionに model containerを足し model typeを供給します そしてview codeで犬の配列を Query dynamic propertyに切り替えます QueryはSwiftDataにデータベースから model valueのfetchを指示します 新しい犬に会った時のように データが変わると viewは無効になります Queryは大量のデータセットに対し 非常に効率がよく データの戻り方をカスタム化できます 出会った犬のデータでの ソートの順序変えがその例で アプリ体験を向上させることができます SwiftDataはmacOSとiOSでの 書類のデータ保存にも 非常に適しています 犬のタグをビジュアライズする 簡単なプロトタイプ方法が アプリに必要だと思い Curtやデザイナーとコラボし このドキュメントベースのアプリを 作成しました ドキュメントベースのアプリは initializerでSwiftDataの 機能すべてを利用出来ます SwiftUIは各ドキュメントのストレージに SwiftDataを使いmodel containerを 自動的に設定します SwiftDataとSwiftUIとの 統合については 「Meet SwiftData」と 「Build an app with SwiftData」を ご覧ください SwiftDataサポートに加え DocumentGroupは iOS 17もしくはiPadOS 17上で 数々のplatform affordanceを得ます automatic sharingやdocument renaming またツールバーのundo controlsなどです Inspectorは現在のselection及びcontextの 詳細を表する新しいmodifierです インターフェイスに distinct sectionとして表されます macOSでは Inspectorは trailing sidebarとして現れます Regular size classのiPad OSでもです Compact size classでは sheetとして現れます Inspectorの詳細については 「Inspectors in SwiftUI: discover the details」をご覧ください ダイアログはiOS 17とmacOS Sonomaで 幾つかの新しいcustomization APIが 加わりました ここではimage export dialogに 便利な情報を与えるために 新しいmodifierを使っています Confirmation labelがその一つです 厳密さの強化によって 重要な確認ダイアログに注意を引き suppression toggleを足すことで それに続くやりとりには ダイアログは表さないという 設定が可能です 最後にHelpLinkの追加で ダイアログの目的について さらなる情報へのガイドとなります リストやテーブルは多くのアプリに重要で iOS 17とmacOS Sonomaで SwiftUIにそれらを微調整するための 新しい機能とAPIが加わります Tablesは属性順序と表示のカスタマイズを サポートします SceneStorage dynamic propertyを加えれば これらの設定は アプリを通して持続します Customization state valueのある テーブルを供給し 各columnに独自の stable identifierを割り当てます またTablesにはOutlineGroupの パワーが組み込まれています これは階層構成の 大型データに最適で お気に入りの犬とその飼い主の グループ化がその例です 単にDisclosureTableRowを使って 別の列を含む 列を示し 通常通り残りのテーブルを作成します リスト及びテーブル内のSectionsは programmatic expansionをサポートします アプリのサイドバーに使用し Locationsセクションを 拡張できるものの 最初は閉じた状態で現れます 新しいInitializerはセクションでの 現expansion stateを反映する値を バインディングします 小さいデータセットでも Tablesにrow backgroundsや column headersが どう表示させるかというような styling affordanceが加わりました 最後にstar ratingのような カスタムコントロールも background prominence environment propertyの恩恵を得ます 背景が目立つ時に それほど目立たない 前景スタイルを使用するのは リストのカスタムコントロールに最適です リストとテーブルの微調整をする これらの機能やAPIに加え 性能にも大きな改善を加えました 特に大きいデータセットを扱う時です このことやSwiftUI viewsの 最適化については 「Demystify SwiftUI performance」を ご覧ください ObservableやSwiftDataにInspector そしてテーブルのカスタマイズまで データの扱いが新体験のようになります Jeffが作成した データモデルとテーブルで 素晴らしいアプリの骨組みができました さらにアニメーションAPIの お話をしましょう 犬の写真を眺められる Apple TVアプリはどうでしょう これはviewerを選ぶ時の アニメーションです Keyframe Animator APIを使って 作成しました Keyframe animatorsは いくつものプロパティを並行して アニメーション化することができます Animatorに animatable propertiesを含むvalueと equatable stateを与えます Stateの変化でアニメーションします 最初のクロージャでは animatable propertiesで 変更したviewを作成しました その例がロゴの縦位置オフセットです 次のクロージャで これらのpropertiesが時間を経て どう変わるか定義します 例えば最初のtrackは erticalTranslation propertの アニメーションを定義します Spring animationを使い 最初の4分の1秒で ロゴを30ポイント下げます そしてcubic curveでビーグルを ジャンプそして着地させます 最後にspring animationで 元の位置に戻します ほかのanimated propertiesも さらなるtracksで定義します これらのtracksを並行に作動させ完成です Keyframe animatorsの活用については 「Wind your way through advanced animations in SwiftUI」をご覧ください ジョギング中に出会った犬の 記録のためにApple Watchアプリも 作っています 今のところハッピーなアイコンと 出会いを記録するボタンだけです ボタンをタップした時にアニメーションさせましょう ここではPhase animatorが役に立ちます Phase animatorは keyframe animatorよりシンプルです Parallel tracksに代わりに 一連のフェーズのシーケンスを 順にたどります 1つのアニメーションが終われば 次のアニメーションを始められます Animatorにフェーズのシーケンスを与え sightingCountが変われば アニメーション開始するよう伝えます そして最初のクロージャで 現在のフェーズに基づき 犬のrotationとscaleを設定します 次のクロージャでSwiftUIに各フェーズに どうアニメーションさせるのかを伝えます ここではナイスな spring animationsを使っています これらの名前を私は気に入ってます snappyやbouncy animationなんて 使いたくなるでしょう そしてgrow phaseには custom springを使用しています Springsはdurationとbounceを 設定でき説明が楽になりました SwiftUI animationが使えるなら どこでもこれらのsprinsを使用できます Spring animationsには自然感があり これまでのanimationと匹敵する速さで リアルな摩擦で最終の値に落ち着きます これがiOS 17以降に 作成されたアプリの デフォルトアニメーションです アニメーションは完成です でもジョギング中ですので 感覚フィードバックがあるべきです 感覚フィードバックはタップのような 触知反応を提供し 注意を引いてアクションや イベントを強化します 手首のタップで犬を逃さなかったと 確信が持てます Sensory feedback APIで 感覚フィードバックも簡単です 感覚フィードバックを足すには sensoryFeedback modifierを付け どのようなフィードバックが いつ起こるべきかを特定します sensoryFeedback modifierは 感覚フィードバックをサポートする すべてのプラットフォームで使用できます プラットフォームによって フィードバックが違います 「Human Interface Guidelines」で どのフィードバックが アプリに適切か学べます またスタート画面の動画も visual effects modifierを使って 作っています Visual effects modifierは これらの犬の写真を位置に基づき更新します GeometryReaderは必要ありません 焦点を画面中動かすsimulationを 用いています 焦点とはこの赤い点のことです 犬を表示しているこの基盤目と 座標空間を関連付けます そしてDogCircle view内に visual effectを足します クロージャに変更するコンテンツと geometry proxyを取り込みます Geometry proxyをhelper methodにパスし scaleを計算します Geometry proxyで基盤目のviewと それと比べた1つの犬の円の フレームのサイズを得ることができます それによりシミュレーションの 焦点からの犬の距離を計算でき 焦点の犬をスケールアップできます これらすべてをvisual effectsで GeometryReaderを使わず行えます そして自動的に違ったサイズに適応できます

    もう一つ例があります 出会った犬の飼い主に いいワンちゃんメッセージを送る機能を 調整していますが 犬の名前にスタイルを加え 目立たせてみましょう これは別のtext view内でtextを foreground styleに挿入できるので簡単です 見てください スタイリングを このスライダーで調節できます でもどうやって? スタイルの定義はこうです stripeSpacingとangleを asset catalogのカラーと共に custom Metal shaderにパスします SwiftUIの新しいShaderLibraryで Metal shader functionsを 直接SwiftUI shape stylesに変換できます Furdinandの名前に ストライプをレンダリングするようにです

    Metal shadersを使用したいなら プロジェクトに新規Metal fileを足し SwiftUIのShaderLibraryandで shader functionを呼び出します またここには もう一ついい例があります スライダーが端に達すると シンボルがバウンドします この効果はmacOSとiOSの Sliderに組み込まれています またsymbol effect modifierで 独自のシンボルに足すこともできます このmodifierをSF Symbolか view hierarchyの すべてのシンボルに適用します Symbolsは多くの効果をサポートし pulseやvariable colorなどの continuous animationsも含みます Scaleで大きさを変えたり appear/disappearや replaceもあります bounce付きevent notificationsもあります 「Animate symbols in your app」で symbol effectsを使って みんなを喜ばす方法を 学んでみてください この例を閉じる前に もう一つの機能をご紹介します ここに単位がありますね これまでは小型英大文字を使っていましたが これからはこのルックスを 単位にtextScale modifierを適応して 得られるようになりました もしこのアプリを中国市場に持ち込むなら 中国の印刷術には 小型英大文字の概念はありませんが 単位は正しい大きさに調整されます ほかにも複数地域に対応させる ツールがあります タイ語などでは長い文字型を使用します これらの言語のテキストが 英語のような短い文字型の言語のテキストに ローカライズされると 長いテキストは幅がなかったり 切り落とされます この問題が起こり得る場合 例えば犬の名前が世界中から クラウドソースされた場合 typesettingLanguage modifierを 適応できます これはテキストにスペースが必要だと SwiftUIに伝えます これらの新APIの使用は楽しいですが 人々を圧倒させないよう アニメーションの選択には気をつけましょう SwiftUIでのアニメーションの 基本については 「Explore SwiftUI animation」を ご覧ください そして「Animate with springs」で Jacobがあらゆるデバイスでの アニメーションの作成について 教えてくれます SwiftUIの新アニメーションAPIは 素晴らしいものです これは氷山の一角で animation completion handlersから カスタム動画の構築まで まだまだたくさんあります みなさんもこれらのAPIをご活用ください これらの新しいアニメーションや効果で アプリがさらに向上します それでは最後の仕上げのための インタラクションAPIを 見てみましょう インタラクションはアプリ体験のコアで これらはiOS 17及びそれ以降のリリースで アップデートされるAPIの一部です 最近出会った犬の画面には 最後の仕上げが必要です Scroll viewで犬のカードをめくる際 出入りするカードに何らかの効果を 足してみましょう Scroll transition modifierは 先程Curtがオープン画面で使った visual effect modifierに似ています ではscroll viewでeffectsを 適用してみましょう Scaleとopacity effectsで 少しのコードを足しただけで 求めていた最後の仕上げができました 好きなドッグランのリストを side-scrolling listでこの画面に足します SwiftUIにこれができる 素晴らしい機能が追加されました Dog cardのvertical stackの上に park cardのhorizontal stackを足します ここでは containerRelativeFrame modifierで これらのpark cardsを horizontal scroll viewのサイズに スケールさせます Countは画面を幾つかの塊に 分割するか特定します Spanは幾つの塊を viewに取り入れるかを指定します これはなかなかですね でもpark cardsをパチンと 位置にはめたいとします 新機能のscrollTargetLayout modifierで簡単です LazyHStackに足しscroll viewを変更し targeted layoutのviewsと整列させます View alignmentに加え scroll viewsはpaging behaviorを 使用するよう定義できます カスタマイズするために 独自のbehaviorを scrollTargetBehavior protocolで 定義できます またscroll viewでトップの犬を 少し褒めてあげましょう 新しいscrollPosition modifierで トップの犬のIDをバインディングします スクロールで更新されていますね いつでもどの犬がトップかわかります Scroll viewでのこれらの改良点について 学びたければ 「Beyond scroll views」をご覧ください Imageはハイダイナミックレンジの コンテンツのレンダリングをサポートします allowedDynamicRange modifierで フィデリティーを維持したままの 美しいイメージが映し出されます しかし際立つイメージに 時折使用するくらいにしましょう SwiftUIで書かれたアプリは アクセシビリティ機能を そのまま使用できますが これからご紹介する 新しいアクセシビリティAPIで さらに向上できます この冒険好きな犬は 少し小さくて見えないので magnification gesture機能で ズームできるようにしました またviewに accessibilityZoomAction modifierを足します これによりVoiceOverのような支援技術が ジェスチャーを使わずに同じ機能に アクセスできるようになります アクションの方向次第で ズームレベルをアップデートすると この冒険好きが何をしてるかが 見えますね VoiceOver:image viewにズーム Appleのプラットフォームの アクセシビリティ機能について 是非とも 「Build accessible apps with SwiftUI and UIKit」をご覧ください Colorは静的メンバー構文を使って アプリのasset catalogで定義された カスタムカラーを調べることができます 使用中はcompile-time safetyで タイポで時間を無駄にしなくなります 先程お見せしたドキュメントアプリで 便利なアクションを含むメニューを ツールバーに足しました メニューのトップ部分はControlGroupで 新しいcompactMenu styleがあり アイテムをアイコンとして 水平スタックの中に表示します Tag color selectorは 新しいpalette styleのある pickerとして定義されます これをシンボルと使用すれば メニューに素晴らしい視覚表現を与えます 特にこのようにラベルに色合いを使って 区別させることができます 最後にpaletteSelectionEffect modifierで pickerで特定のものを示すsymbol variantを 使用することができます メニューが完成し Buddyの鑑札が お気に入りの色になりました テニスボールの黄色です Bordered buttonsは組み込みの形で 円形や角丸長方形など 定義できるようになりました これらのborder shape stylesは iOSとwatchOSとmacOSで使用できます macOSとiOSのボタンは ドラッグに反応するようになり editorのこのボタンのように ポップオーバーが開けます 新しいspringLoadingBehavior modifierは ドラッグポーズしたり macOSでフォースクリックでボタンを押すと アクションを起こすようになっています tvOSのボタンには highlight hover effectが加わります ギャラリーで使用しましたが button labelの image部分だけに適用し このプラットフォームに最適です このボタンのborderless styleは 今回からtvOSに追加されました ハードウェアキーボードはアプリでの対話を 促進させるのに役立つツールです ハードウェアキーボードをサポートする プラットフォームのfocusable viewsは onKeyPress modifierを使って どのキーボード入力にも直接反応できます Modifierはキー入力がマッチすれば アクションを起こします Focusのレシピを知りたければ 「The SwiftUI cookbook for focus」を ご覧ください Scroll transitionsやbehaviors ボタンからフォーカスまで これらのAPIで豊富な機能と 素晴らしいスタイルのアプリを作成できます 僕らのアプリはかなり進展したね ああ 確かに 新しいAPIは楽しいしね それも確かだ SwiftUIは今が面白い時です 新しいプラットフォーム! ObservableとSwiftDataの優雅さは SwiftUIにピッタリです アニメーションの改善もすごいね Scroll viewsも! これらのAPIで デベロッパコミュニティが 何を作り出すか楽しみだね ありがとうございました みなさんの犬によろしく! みなさん 頑張って! ♪

    • 4:49 - watchOS 10

      import SwiftUI
      
      #if os(watchOS)
      struct ContainerBackground_Snippet: View {
          @State private var selection: Int?
          @State var date = Date()
      
          var body: some View {
              NavigationSplitView {
                  List(selection: $selection) {
                      NavigationLink("Dates", value: -1)
                      NavigationLink("Zero", value: 0)
                      NavigationLink("One", value: 1)
                      NavigationLink("Two", value: 2)
                  }
                  .containerBackground(
                      Color.green.gradient, for: .navigation)
              } detail: {
                  switch selection {
                  case -1:
                      DatePicker(
                          "Time",
                          selection: $date,
                          displayedComponents: .hourMinuteAndSecond)
                      .containerBackground(
                          Color.yellow.gradient, for: .navigation)
                  case let value?:
                      DetailView(value: value)
                          .containerBackground(
                              Color.blue.gradient, for: .navigation)
                  default:
                      Text("Choose a link")
                  }
              }
          }
      
          struct DetailView: View {
              var value: Int
      
              var body: some View {
                  Text("\(value)")
                      .font(.largeTitle)
              }
          }
      }
      
      #Preview {
          ContainerBackground_Snippet()
      }
      #endif
    • 7:01 - Widget Previews

      #Preview(as: .systemSmall) {
          CaffeineTrackerWidget()
      } timeline: {
          CaffeineLogEntry.log1
          CaffeineLogEntry.log2
          CaffeineLogEntry.log3
          CaffeineLogEntry.log4
      }
    • 7:28 - SwiftUI Preview

      #Preview("good dog") {
          ZStack(alignment: .bottom) {
              Rectangle()
                  .fill(Color.blue.gradient)
              Text("Riley")
                  .font(.largeTitle)
                  .padding()
                  .background(.thinMaterial, in: .capsule)
                  .padding()
          }
          .ignoresSafeArea()
      }
    • 7:33 - Mac Preview

      import SwiftUI
      
      struct MacPreview_Snippet: View {
          @State private var drinks = Drink.sampleData
          @State private var selection: Drink?
      
          var body: some View {
              NavigationSplitView {
                  List(drinks, selection: $selection) { drink in
                      NavigationLink(drink.name, value: drink)
                  }
              } detail: {
                  if let selection {
                      DrinkCard(drink: selection)
                  } else {
                      ContentUnavailableView(
                          "Select a drink",
                          systemImage: "cup.and.saucer.fill")
                  }
              }
          }
      }
      
      struct DrinkCard: View {
          var drink: Drink
      
          var body: some View {
              ZStack(alignment: .top) {
                  Rectangle()
                      .fill(Color.blue.gradient)
                  Text(drink.name)
                      .padding([.leading, .trailing], 16)
                      .padding([.top, .bottom], 4)
                      .background(.thinMaterial, in: .capsule)
                      .padding()
              }
          }
      }
      
      struct Drink: Identifiable, Hashable {
          let id = UUID()
          var name: String
      
          static let sampleData: [Drink] = [
              Drink(name: "Cappuccino"),
              Drink(name: "Coffee"),
              Drink(name: "Espresso"),
              Drink(name: "Latte"),
              Drink(name: "Macchiato"),
          ]
      }
      
      #Preview {
          MacPreview_Snippet()
      }
    • 8:18 - MapKit

      import SwiftUI
      import MapKit
      
      struct Maps_Snippet: View {
          private let location = CLLocationCoordinate2D(
              latitude: CLLocationDegrees(floatLiteral: 37.3353),
              longitude: CLLocationDegrees(floatLiteral: -122.0097))
      
          var body: some View {
              Map {
                  Marker("Pond", coordinate: location)
                  UserAnnotation()
              }
              .mapControls {
                  MapUserLocationButton()
                  MapCompass()
              }
          }
      }
      
      #Preview {
          Maps_Snippet()
      }
    • 8:46 - Scrolling Charts

      import SwiftUI
      import Charts
      
      struct ScrollingChart_Snippet: View {
          @State private var scrollPosition = SalesData.last365Days.first!
          @State private var selection: SalesData?
      
          var body: some View {
              VStack(alignment: .leading) {
                  VStack(alignment: .leading) {
                      Text("""
                          Scrolled to: \
                          \(scrollPosition.day,
                              format: .dateTime.day().month().year())
                          """)
                      Text("""
                          Selected: \
                          \(selection?.day ?? .now,
                              format: .dateTime.day().month().year())
                          """)
                      .opacity(selection != nil ? 1.0 : 0.0)
                  }
                  .padding([.leading, .trailing])
                  Chart {
                      ForEach(SalesData.last365Days, id: \.day) {
                          BarMark(
                              x: .value("Day", $0.day, unit: .day),
                              y: .value("Sales", $0.sales))
                      }
                      .foregroundStyle(.blue)
                  }
                  .chartScrollableAxes(.horizontal)
                  .chartXVisibleDomain(length: 3600 * 24 * 30)
                  .chartScrollPosition(x: $scrollPosition)
                  .chartXSelection(value: $selection)
              }
          }
      }
      
      struct SalesData: Plottable {
          var day: Date
          var sales: Int
      
          var primitivePlottable: Date { day }
      
          init?(primitivePlottable: Date) {
              self.day = primitivePlottable
              self.sales = 0
          }
      
          init(day: Date, sales: Int) {
              self.day = day
              self.sales = sales
          }
      
          static let last365Days: [SalesData] = buildSalesData()
      
          private static func buildSalesData() -> [SalesData] {
              var result: [SalesData] = []
              var date = Date.now
              for _ in 0..<365 {
                  result.append(SalesData(day: date, sales: Int.random(in: 150...250)))
                  date = Calendar.current.date(
                      byAdding: .day, value: -1, to: date)!
              }
              return result.reversed()
          }
      }
      
      
      #Preview {
          ScrollingChart_Snippet()
      }
    • 9:00 - Donut and Pie Charts

      import SwiftUI
      import Charts
      
      struct DonutChart_Snippet: View {
          var sales = Bagel.salesData
      
          var body: some View {
              NavigationStack {
                  Chart(sales, id: \.name) { element in
                      SectorMark(
                          angle: .value("Sales", element.sales),
                          innerRadius: .ratio(0.6),
                          angularInset: 1.5)
                      .cornerRadius(5)
                      .foregroundStyle(by: .value("Name", element.name))
                  }
                  .padding()
                  .navigationTitle("Bagel Sales")
                  .toolbarTitleDisplayMode(.inlineLarge)
              }
          }
      }
      
      struct Bagel {
          var name: String
          var sales: Int
      
          static var salesData: [Bagel] = buildSalesData()
      
          static func buildSalesData() -> [Bagel] {
              [
                  Bagel(name: "Blueberry", sales: 60),
                  Bagel(name: "Everything", sales: 120),
                  Bagel(name: "Choc. Chip", sales: 40),
                  Bagel(name: "Cin. Raisin", sales: 100),
                  Bagel(name: "Plain", sales: 140),
                  Bagel(name: "Onion", sales: 70),
                  Bagel(name: "Sesame Seed", sales: 110),
              ]
          }
      }
      
      #Preview {
          DonutChart_Snippet()
      }
    • 9:31 - StoreKit

      import SwiftUI
      import StoreKit
      
      struct SubscriptionStore_Snippet {
          var body: some View {
              SubscriptionStoreView(groupID: passGroupID) {
                  PassMarketingContent()
                      .lightMarketingContentStyle()
                      .containerBackground(for: .subscriptionStoreFullHeight) {
                          SkyBackground()
                      }
              }
              .backgroundStyle(.clear)
              .subscriptionStoreButtonLabel(.multiline)
              .subscriptionStorePickerItemBackground(.thinMaterial)
              .storeButton(.visible, for: .redeemCode)
          }
      }
    • 10:56 - Observable Model

      import Foundation
      import SwiftUI
      
      @Observable
      class Dog: Identifiable {
          var id = UUID()
          var name = ""
          var age = 1
          var breed = DogBreed.mutt
          var owner: Person? = nil
      }
      
      class Person: Identifiable {
          var id = UUID()
          var name = ""
      }
      
      enum DogBreed {
          case mutt
      }
    • 11:22 - Observable View Integration

      import Foundation
      import SwiftUI
      
      struct DogCard: View {
          var dog: Dog
          
          var body: some View {
              DogImage(dog: dog)
                  .overlay(alignment: .bottom) {
                      HStack {
                          Text(dog.name)
                          Spacer()
                          Image(systemName: "heart")
                              .symbolVariant(dog.isFavorite ? .fill : .none)
                      }
                      .font(.headline)
                      .padding(.horizontal, 22)
                      .padding(.vertical, 12)
                      .background(.thinMaterial)
                  }
                  .clipShape(.rect(cornerRadius: 16))
          }
          
          struct DogImage: View {
              var dog: Dog
      
              var body: some View {
                  Rectangle()
                      .fill(Color.green)
                      .frame(width: 400, height: 400)
              }
          }
      
          @Observable
          class Dog: Identifiable {
              var id = UUID()
              var name = ""
              var isFavorite = false
          }
      }
    • 12:22 - Observable State Integration

      import Foundation
      import SwiftUI
      
      struct AddSightingView: View {
          @State private var model = DogDetails()
      
          var body: some View {
              Form {
                  Section {
                      TextField("Name", text: $model.dogName)
                      DogBreedPicker(selection: $model.dogBreed)
                  }
                  Section {
                      TextField("Location", text: $model.location)
                  }
              }
          }
      
          struct DogBreedPicker: View {
              @Binding var selection: DogBreed
      
              var body: some View {
                  Picker("Breed", selection: $selection) {
                      ForEach(DogBreed.allCases) {
                          Text($0.rawValue.capitalized)
                              .tag($0.id)
                      }
                  }
              }
          }
      
          @Observable
          class DogDetails {
              var dogName = ""
              var dogBreed = DogBreed.mutt
              var location = ""
          }
      
          enum DogBreed: String, CaseIterable, Identifiable {
              case mutt
              case husky
              case beagle
      
              var id: Self { self }
          }
      }
      
      #Preview {
          AddSightingView()
      }
    • 12:33 - Observable Environment Integration

      import SwiftUI
      
      @main
      private struct WhatsNew2023: App {
          @State private var currentUser: User?
          
          var body: some Scene {
              WindowGroup {
                  ContentView()
                      .environment(currentUser)
              }
          }
          
          struct ContentView: View {
              var body: some View {
                  Color.clear
              }
          }
      
          struct ProfileView: View {
              @Environment(User.self) private var currentUser: User?
      
              var body: some View {
                  if let currentUser {
                      UserDetails(user: currentUser)
                  } else {
                      Button("Log In") { }
                  }
              }
          }
      
          struct UserDetails: View {
              var user: User
      
              var body: some View {
                  Text("Hello, \(user.name)")
              }
          }
      
          @Observable
          class User: Identifiable {
              var id = UUID()
              var name = ""
          }
      }
    • 13:59 - SwiftData Model Container

      import Foundation
      import SwiftUI
      import SwiftData
      
      @main
      private struct WhatsNew2023: App {
          
          var body: some Scene {
              WindowGroup {
                  ContentView()
              }
              .modelContainer(for: Dog.self)
          }
          
          struct ContentView: View {
              var body: some View {
                  Color.clear
              }
          }
      
          @Model
          class Dog {
              var name = ""
              var age = 1
          }
      }
    • 14:05 - SwiftData Query

      import Foundation
      import SwiftUI
      import SwiftData
      
      struct RecentDogsView: View {
          @Query(sort: \.dateSpotted) private var dogs: [Dog]
      
          var body: some View {
              ScrollView(.vertical) {
                  LazyVStack {
                      ForEach(dogs) { dog in
                          DogCard(dog: dog)
                      }
                  }
              }
          }
      
          struct DogCard: View {
              var dog: Dog
              
              var body: some View {
                  DogImage(dog: dog)
                      .overlay(alignment: .bottom) {
                          HStack {
                              Text(dog.name)
                              Spacer()
                              Image(systemName: "heart")
                                  .symbolVariant(dog.isFavorite ? .fill : .none)
                          }
                          .font(.headline)
                          .padding(.horizontal, 22)
                          .padding(.vertical, 12)
                          .background(.thinMaterial)
                      }
                      .clipShape(.rect(cornerRadius: 16))
              }
          }
      
          struct DogImage: View {
              var dog: Dog
      
              var body: some View {
                  Rectangle()
                      .fill(Color.green)
                      .frame(width: 400, height: 400)
              }
          }
      
          @Model
          class Dog: Identifiable {
              var name = ""
              var isFavorite = false
              var dateSpotted = Date.now
          }
      }
      
      #Preview {
          RecentDogsView()
      }
    • 14:52 - SwiftData DocumentGroup

      import SwiftUI
      import SwiftData
      import UniformTypeIdentifiers
      
      @main
      private struct WhatsNew2023: App {
          var body: some Scene {
              DocumentGroup(editing: DogTag.self, contentType: .dogTag) {
                  ContentView()
              }
          }
          
          struct ContentView: View {
              var body: some View {
                  Color.clear
              }
          }
      
          @Model
          class DogTag {
              var text = ""
          }
      }
      
      extension UTType {
          static var dogTag: UTType {
              UTType(exportedAs: "com.apple.SwiftUI.dogTag")
          }
      }
    • 15:33 - Inspector

      import SwiftUI
      
      struct InspectorContentView: View {
          @State private var inspectorPresented = true
          
          var body: some View {
              DogTagEditor()
                  .inspector(isPresented: $inspectorPresented) {
                      DogTagInspector()
                  }
          }
          
          struct DogTagEditor: View {
              var body: some View {
                  Color.clear
              }
          }
          
          struct DogTagInspector: View {
              @State private var fontName = FontName.sfHello
              @State private var fontColor: Color = .white
              
              var body: some View {
                  Form {
                      Section("Text Formatting") {
                          Picker("Font", selection: $fontName) {
                              ForEach(FontName.allCases) {
                                  Text($0.name).tag($0)
                              }
                          }
      
                          ColorPicker("Font Color", selection: $fontColor)
                      }
                  }
              }
          }
      
          enum FontName: Identifiable, CaseIterable {
              case sfHello
              case arial
              case helvetica
      
              var id: Self { self }
              var name: String {
                  switch self {
                  case .sfHello: return "SF Hello"
                  case .arial: return "Arial"
                  case .helvetica: return "Helvetica"
                  }
              }
          }
      }
      
      #Preview {
          InspectorContentView()
      }
    • 16:10 - File Export Dialog Customization

      import Foundation
      import SwiftUI
      import UniformTypeIdentifiers
      
      struct ExportDialogCustomization: View {
          @State private var isExporterPresented = true
          @State private var selectedItem = ""
          
          var body: some View {
              Color.clear
                  .fileExporter(
                      isPresented: $isExporterPresented, item: selectedItem,
                      contentTypes: [.plainText], defaultFilename: "ExportedData.txt")
                  { result in
                      handleDataExport(result: result)
                  }
                  .fileExporterFilenameLabel("Export Data")
                  .fileDialogConfirmationLabel("Export Data")
          }
      
          func handleDataExport(result: Result<URL, Error>) {
          }
      
          struct Data: Codable, Transferable {
              static var transferRepresentation: some TransferRepresentation {
                  CodableRepresentation(contentType: .plainText)
              }
      
              var text = "Exported Data"
          }
      }
    • 16:19 - Confirmation Dialog Customization

      import Foundation
      import SwiftUI
      import UniformTypeIdentifiers
      
      struct ConfirmationDialogCustomization: View {
          @State private var showDeleteDialog = false
          @AppStorage("dialogIsSuppressed") private var dialogIsSuppressed = false
      
          var body: some View {
              Button("Show Dialog") {
                  if !dialogIsSuppressed {
                      showDeleteDialog = true
                  }
              }
              .confirmationDialog(
                  "Are you sure you want to delete the selected dog tag?",
                  isPresented: $showDeleteDialog)
              {
                  Button("Delete dog tag", role: .destructive) { }
      
                  HelpLink { }
              }
              .dialogSeverity(.critical)
              .dialogSuppressionToggle(isSuppressed: $dialogIsSuppressed)
          }
      }
    • 17:01 - Table Column Customization

      import SwiftUI
      
      struct DogSightingsTable: View {
          private var dogSightings: [DogSighting] = (1..<50).map {
              .init(
                  name: "Sighting \($0)",
                  date: .now + Double((Int.random(in: -5..<5) * 86400)))
          }
      
          @SceneStorage("columnCustomization")
          private var columnCustomization: TableColumnCustomization<DogSighting>
          @State private var selectedSighting: DogSighting.ID?
          
          var body: some View {
              Table(
                  dogSightings, selection: $selectedSighting,
                  columnCustomization: $columnCustomization)
              {
                  TableColumn("Dog Name", value: \.name)
                      .customizationID("name")
                  
                  TableColumn("Date") {
                      Text($0.date, style: .date)
                  }
                  .customizationID("date")
              }
          }
          
          struct DogSighting: Identifiable {
              var id = UUID()
              var name: String
              var date: Date
          }
      }
    • 17:22 - DisclosureTableRow

      import SwiftUI
      
      struct DogGenealogyTable: View {
          private static let dogToys = ["🦴", "🧸", "👟", "🎾", "🥏"]
      
          private var dogs: [DogGenealogy] = (1..<10).map {
              .init(
                  name: "Parent \($0)", age: Int.random(in: 8..<12) * 7,
                  favoriteToy: dogToys[Int.random(in: 0..<5)],
                  children: (1..<10).map {
                      .init(
                          name: "Child \($0)", age: Int.random(in: 1..<5) * 7,
                          favoriteToy: dogToys[Int.random(in: 0..<5)])
                  }
              )
          }
      
          var body: some View {
              Table(of: DogGenealogy.self) {
                  TableColumn("Dog Name", value: \.name)
                  TableColumn("Age (Dog Years)") {
                      Text($0.age, format: .number)
                  }
                  TableColumn("Favorite Toy", value: \.favoriteToy)
              } rows: {
                  ForEach(dogs) { dog in
                      DisclosureTableRow(dog) {
                          ForEach(dog.children) { child in
                              TableRow(child)
                          }
                      }
                  }
              }
          }
      
          struct DogGenealogy: Identifiable {
              var id = UUID()
              var name: String
              var age: Int
              var favoriteToy: String
              var children: [DogGenealogy] = []
          }
      }
    • 17:45 - Programmatic Section Expansion

      import SwiftUI
      
      struct ExpandableSectionsView: View {
          @State private var selection: Int?
      
          var body: some View {
              NavigationSplitView {
                  Sidebar(selection: $selection)
              } detail: {
                  Detail(selection: selection)
              }
          }
      
          struct Sidebar: View {
              @Binding var selection: Int?
              @State private var isSection1Expanded = true
              @State private var isSection2Expanded = false
      
              var body: some View {
                  List(selection: $selection) {
                      Section("First Section", isExpanded: $isSection1Expanded) {
                          ForEach(1..<6, id: \.self) {
                              Text("Item \($0)")
                          }
                      }
                      Section("Second Section", isExpanded: $isSection2Expanded) {
                          ForEach(6..<11, id: \.self) {
                              Text("Item \($0)")
                          }
                      }
                  }
              }
          }
      
          struct Detail: View {
              var selection: Int?
      
              var body: some View {
                  Text(selection.map { "Selection: \($0)" } ?? "No Selection")
              }
          }
      }
    • 17:54 - Table Display Customization And Background Prominence

      import SwiftUI
      
      struct TableDisplayCustomizationView: View {
          private var dogSightings: [DogSighting] = (1..<10).map {
              .init(
                  name: "Dog Breed \($0)", sightings: Int.random(in: 1..<5),
                  rating: Int.random(in: 1..<6))
          }
      
          @State private var selection: DogSighting.ID?
      
          var body: some View {
              Table(dogSightings, selection: $selection) {
                  TableColumn("Name", value: \.name)
                  TableColumn("Sightings") {
                      Text($0.sightings, format: .number)
                  }
                  TableColumn("Rating") {
                      StarRating(rating: $0.rating)
                          .foregroundStyle(.starRatingForeground)
                  }
              }
              .alternatingRowBackgrounds(.disabled)
              .tableColumnHeaders(.hidden)
          }
      
          struct StarRating: View {
              var rating: Int
      
              var body: some View {
                  HStack(spacing: 1) {
                      ForEach(1...5, id: \.self) { n in
                          Image(systemName: "star")
                              .symbolVariant(n <= rating ? .fill : .none)
                      }
                  }
                  .imageScale(.small)
              }
          }
      
          struct StarRatingForegroundStyle: ShapeStyle {
              func resolve(in environment: EnvironmentValues) -> some ShapeStyle {
                  if environment.backgroundProminence == .increased {
                      return AnyShapeStyle(.secondary)
                  } else {
                      return AnyShapeStyle(.yellow)
                  }
              }
          }
      
          struct DogSighting: Identifiable {
              var id = UUID()
              var name: String
              var sightings: Int
              var rating: Int
          }
      }
      
      extension ShapeStyle where Self ==
          TableDisplayCustomizationView.StarRatingForegroundStyle
      {
          static var starRatingForeground: TableDisplayCustomizationView.StarRatingForegroundStyle {
              .init()
          }
      }
    • 19:19 - Keyframe Animator

      import SwiftUI
      
      struct KeyframeAnimator_Snippet: View {
          var body: some View {
              Logo(color: .blue)
              Text("Tap the shape")
          }
      }
      
      struct Logo: View {
          var color: Color
          @State private var runPlan = 0
      
          var body: some View {
              VStack(spacing: 100) {
                  KeyframeAnimator(
                      initialValue: AnimationValues(), trigger: runPlan
                  ) { values in
                      LogoField(color: color)
                          .scaleEffect(values.scale)
                          .rotationEffect(values.rotation, anchor: .bottom)
                          .offset(y: values.verticalTranslation)
                          .frame(width: 240, height: 240)
                  } keyframes: { _ in
                      KeyframeTrack(\.verticalTranslation) {
                          SpringKeyframe(30, duration: 0.25, spring: .smooth)
                          CubicKeyframe(-120, duration: 0.3)
                          CubicKeyframe(-120, duration: 0.5)
                          CubicKeyframe(10, duration: 0.3)
                          SpringKeyframe(0, spring: .bouncy)
                      }
      
                      KeyframeTrack(\.scale) {
                          SpringKeyframe(0.98, duration: 0.25, spring: .smooth)
                          SpringKeyframe(1.2, duration: 0.5, spring: .smooth)
                          SpringKeyframe(1.0, spring: .bouncy)
                      }
      
                      KeyframeTrack(\.rotation) {
                          LinearKeyframe(Angle(degrees:0), duration: 0.45)
                          CubicKeyframe(Angle(degrees: 0), duration: 0.1)
                          CubicKeyframe(Angle(degrees: -15), duration: 0.1)
                          CubicKeyframe(Angle(degrees: 15), duration: 0.1)
                          CubicKeyframe(Angle(degrees: -15), duration: 0.1)
                          SpringKeyframe(Angle(degrees: 0), spring: .bouncy)
                      }
                  }
                  .onTapGesture {
                      runPlan += 1
                  }
              }
          }
      
          struct AnimationValues {
              var scale = 1.0
              var verticalTranslation = 0.0
              var rotation = Angle(degrees: 0.0)
          }
      
          struct LogoField: View {
              var color: Color
      
              var body: some View {
                  ZStack(alignment: .bottom) {
                      RoundedRectangle(cornerRadius: 48)
                          .fill(.shadow(.drop(radius: 5)))
                          .fill(color.gradient)
                  }
              }
          }
      }
      
      #Preview {
          KeyframeAnimator_Snippet()
      }
    • 20:35 - Phase Animator

      import SwiftUI
      
      struct PhaseAnimator_Snippet: View {
          @State private var sightingCount = 0
      
          var body: some View {
              VStack {
                  Spacer()
                  HappyDog()
                      .phaseAnimator(
                          SightingPhases.allCases, trigger: sightingCount
                      ) { content, phase in
                          content
                              .rotationEffect(phase.rotation)
                              .scaleEffect(phase.scale)
                      } animation: { phase in
                          switch phase {
                          case .shrink: .snappy(duration: 0.1)
                          case .spin: .bouncy
                          case .grow: .spring(
                              duration: 0.2, bounce: 0.1, blendDuration: 0.1)
                          case .reset: .linear(duration: 0.0)
                          }
                      }
                      .sensoryFeedback(.increase, trigger: sightingCount)
                  Spacer()
                  Button("There’s One!", action: recordSighting)
                      .zIndex(-1.0)
              }
          }
      
          func recordSighting() {
              sightingCount += 1
          }
      
          enum SightingPhases: CaseIterable {
              case reset
              case shrink
              case spin
              case grow
      
              var rotation: Angle {
                  switch self {
                  case .spin, .grow: Angle(degrees: 360)
                  default: Angle(degrees: 0)
                  }
              }
      
              var scale: Double {
                  switch self {
                  case .reset: 1.0
                  case .shrink: 0.75
                  case .spin: 0.85
                  case .grow: 1.0
                  }
              }
          }
      }
      
      struct HappyDog: View {
          var body: some View {
              ZStack(alignment: .center) {
                  Rectangle()
                      .fill(.blue.gradient)
                  Text("🐶")
                      .font(.system(size: 58))
              }
              .clipShape(.rect(cornerRadius: 12))
              .frame(width: 96, height: 96)
          }
      }
      
      #Preview {
          PhaseAnimator_Snippet()
      }
    • 22:27 - Haptic Feedback

      https://developer.apple.com/design/human-interface-guidelines/playing-haptics
    • 22:35 - Visual Effects

      import SwiftUI
      
      struct VisualEffects_Snippet: View {
          @State private var dogs: [Dog] = manySampleDogs
          @StateObject private var simulation = Simulation()
          @State private var showFocalPoint = false
      
          var body: some View {
              ScrollView {
                  LazyVGrid(columns: columns, spacing: itemSpacing) {
                      ForEach(dogs) { dog in
                          DogCircle(dog: dog, focalPoint: simulation.point)
                      }
                  }
                  .opacity(showFocalPoint ? 0.3 : 1.0)
                  .overlay(alignment: .topLeading) {
                      DebugDot(focalPoint: simulation.point)
                          .opacity(showFocalPoint ? 1.0 : 0.0)
                  }
                  .compositingGroup()
              }
              .coordinateSpace(.dogGrid)
              .onTapGesture {
                  withAnimation {
                      showFocalPoint.toggle()
                  }
              }
          }
      
          var columns: [GridItem] {
              [GridItem(
                  .adaptive(
                      minimum: imageLength,
                      maximum: imageLength
                  ),
                  spacing: itemSpacing
              )]
          }
      
          struct DebugDot: View {
              var focalPoint: CGPoint
      
              var body: some View {
                  Circle()
                      .fill(.red)
                      .frame(width: 10, height: 10)
                      .visualEffect { content, proxy in
                          content.offset(position(in: proxy))
                      }
              }
      
              func position(in proxy: GeometryProxy) -> CGSize {
                  guard let backgroundSize = proxy.bounds(of: .dogGrid)?.size else {
                      return .zero
                  }
                  let frame = proxy.frame(in: .dogGrid)
                  let center = CGPoint(
                      x: (frame.minX + frame.maxX) / 2.0,
                      y: (frame.minY + frame.maxY) / 2.0
                  )
                  let xOffset = focalPoint.x * backgroundSize.width - center.x
                  let yOffset = focalPoint.y * backgroundSize.height - center.y
                  return CGSize(width: xOffset, height: yOffset)
              }
          }
      
          /// A self-updating simulation of a point bouncing inside a unit square.
          @MainActor
          class Simulation: ObservableObject {
              @Published var point = CGPoint(
                  x: Double.random(in: 0.001..<1.0),
                  y: Double.random(in: 0.001..<1.0)
              )
      
              private var velocity = CGVector(dx: 0.0048, dy: 0.0028)
              private var updateTask: Task<Void, Never>?
              private var isUpdating = true
      
              init() {
                  updateTask = Task.detached {
                      do {
                          while true {
                              try await Task.sleep(for: .milliseconds(16))
                              await self.updateLocation()
                          }
                      } catch {
                          // fallthrough and exit
                      }
                  }
              }
      
              func toggle() {
                  isUpdating.toggle()
              }
      
              private func updateLocation() {
                  guard isUpdating else { return }
                  point.x += velocity.dx
                  point.y += velocity.dy
                  if point.x < 0 || point.x >= 1.0 {
                      velocity.dx *= -1
                      point.x += 2 * velocity.dx
                  }
                  if point.y < 0 || point.y >= 1.0 {
                      velocity.dy *= -1
                      point.y += 2 * velocity.dy
                  }
              }
          }
      }
      
      extension CoordinateSpaceProtocol where Self == NamedCoordinateSpace {
          fileprivate static var dogGrid: Self { .named("dogGrid") }
      }
      
      private func magnitude(dx: Double, dy: Double) -> Double {
          sqrt(dx * dx + dy * dy)
      }
      
      private struct DogCircle: View {
          var dog: Dog
          var focalPoint: CGPoint
      
          var body: some View {
              ZStack {
                  DogImage(dog: dog)
                      .visualEffect { content, geometry in
                          content
                              .scaleEffect(contentScale(in: geometry))
                              .saturation(contentSaturation(in: geometry))
                              .opacity(contentOpacity(in: geometry))
                      }
              }
          }
      }
      
      private struct DogImage: View {
          var dog: Dog
      
          var body: some View {
              Circle()
                  .fill(.shadow(.drop(
                      color: .black.opacity(0.4),
                      radius: 4,
                      x: 0, y: 2)))
                  .fill(dog.color)
                  .strokeBorder(.secondary, lineWidth: 3)
                  .frame(width: imageLength, height: imageLength)
          }
      }
      
      extension DogCircle {
          func contentScale(in geometry: GeometryProxy) -> Double {
              guard let gridSize = geometry.bounds(of: .dogGrid)?.size else { return 0 }
              let frame = geometry.frame(in: .dogGrid)
              let center = CGPoint(x: (frame.minX + frame.maxX) / 2.0, y: (frame.minY + frame.maxY) / 2.0)
              let xOffset = focalPoint.x * gridSize.width - center.x
              let yOffset = focalPoint.y * gridSize.height - center.y
              let unitMagnitude =
                  magnitude(dx: xOffset, dy: yOffset) / magnitude(dx: gridSize.width, dy: gridSize.height)
              if unitMagnitude < 0.2 {
                  let d = 3 * (unitMagnitude - 0.2)
                  return 1.0 + 1.2 * d * d * (1 + d)
              } else {
                  return 1.0
              }
          }
      
          func contentOpacity(in geometry: GeometryProxy) -> Double {
              opacity(for: displacement(in: geometry))
          }
      
          func contentSaturation(in geometry: GeometryProxy) -> Double {
              opacity(for: displacement(in: geometry))
          }
      
          func opacity(for displacement: Double) -> Double {
              if displacement < 0.3 {
                  return 1.0
              } else {
                  return 1.0 - (displacement - 0.3) * 1.43
              }
          }
      
          func displacement(in proxy: GeometryProxy) -> Double {
              guard let backgroundSize
                  = proxy.bounds(of: .dogGrid)?.size
              else {
                  return 0
              }
              let frame = proxy.frame(in: .dogGrid)
              let center = CGPoint(
                  x: (frame.minX + frame.maxX) / 2.0,
                  y: (frame.minY + frame.maxY) / 2.0
              )
              let xOffset = focalPoint.x * backgroundSize.width - center.x
              let yOffset = focalPoint.y * backgroundSize.height - center.y
              return magnitude(dx: xOffset, dy: yOffset)
                  / magnitude(
                      dx: backgroundSize.width,
                      dy: backgroundSize.height)
          }
      }
      
      private struct Dog: Identifiable {
          let id = UUID()
          var color: Color
      }
      
      private let imageLength = 100.0
      private let itemSpacing = 20.0
      private let possibleColors: [Color] =
          [.red, .orange, .yellow, .green, .blue, .indigo, .purple]
      private let manySampleDogs: [Dog] =
          (0..<100).map {
              Dog(color: possibleColors[$0 % possibleColors.count])
          }
      
      #Preview {
          VisualEffects_Snippet()
      }
    • 23:39 - Metal Shader

      import SwiftUI
      
      struct ShaderUse_Snippet: View {
          @State private var stripeSpacing: Float = 10.0
          @State private var stripeAngle: Float = 0.0
      
          var body: some View {
              VStack {
                  Text(
                      """
                      \(
                          Text("Furdinand")
                              .foregroundStyle(stripes)
                              .fontWidth(.expanded)
                      ) \
                      is a good dog!
                      """
                  )
                  .font(.system(size: 56, weight: .heavy).width(.condensed))
                  .lineLimit(...4)
                  .multilineTextAlignment(.center)
                  Spacer()
                  controls
                  Spacer()
              }
              .padding()
          }
      
          var stripes: Shader {
              ShaderLibrary.angledFill(
                  .float(stripeSpacing),
                  .float(stripeAngle),
                  .color(.blue)
              )
          }
      
          @ViewBuilder
          var controls: some View {
              Grid(alignment: .trailing) {
                  GridRow {
                      spacingSlider
                      ZStack(alignment: .trailing) {
                          Text("50.0 PX").hidden() // maintains size
                          Text("""
                              \(stripeSpacing,
                              format: .number.precision(.fractionLength(1))) \
                              \(Text("PX").textScale(.secondary))
                              """)
                              .foregroundStyle(.secondary)
                      }
                  }
                  GridRow {
                      angleSlider
                      ZStack(alignment: .trailing) {
                          Text("-0.09π RAD").hidden() // maintains size
                          Text("""
                              \(stripeAngle / .pi,
                              format: .number.precision(.fractionLength(2)))π \
                              \(Text("RAD").textScale(.secondary))
                              """)
                              .foregroundStyle(.secondary)
                      }
                  }
              }
              .labelsHidden()
          }
      
          @ViewBuilder
          var spacingSlider: some View {
              Slider(
                  value: $stripeSpacing, in: Float(10.0)...50.0) {
                      Text("Spacing")
                  } minimumValueLabel: {
                      Image(
                          systemName:
                              "arrow.down.forward.and.arrow.up.backward")
                  } maximumValueLabel: {
                      Image(
                          systemName:
                              "arrow.up.backward.and.arrow.down.forward")
                  }
          }
      
          @ViewBuilder
          var angleSlider: some View {
              Slider(
                  value: $stripeAngle, in: (-.pi / 2)...(.pi / 2)) {
                      Text("Angle")
                  } minimumValueLabel: {
                      Image(
                          systemName: "arrow.clockwise")
                  } maximumValueLabel: {
                      Image(
                          systemName: "arrow.counterclockwise")
                  }
          }
      }
      
      // NOTE: create a .metal file in your project and add the following to it:
      
      /*
      
      #include <metal_stdlib>
      using namespace metal;
      
      [[ stitchable ]] half4
      angledFill(float2 position, float width, float angle, half4 color)
      {
          float pMagnitude = sqrt(position.x * position.x + position.y * position.y);
          float pAngle = angle +
              (position.x == 0.0f ? (M_PI_F / 2.0f) : atan(position.y / position.x));
          float rotatedX = pMagnitude * cos(pAngle);
          float rotatedY = pMagnitude * sin(pAngle);
          return (color + color * fmod(abs(rotatedX + rotatedY), width) / width) / 2;
      }
      
       */
      
      #Preview {
          ShaderUse_Snippet()
      }
    • 25:01 - Symbol Effect

      import SwiftUI
      
      struct SymbolEffect_Snippet: View {
          @State private var downloadCount = -2
          @State private var isPaused = false
      
          var scaleUpActive: Bool {
              (downloadCount % 2) == 0
          }
          var isHidden: Bool { scaleUpActive }
          var isShown: Bool { scaleUpActive }
          var isPlaying: Bool { scaleUpActive }
      
          var body: some View {
              ScrollView {
                  VStack(spacing: 48) {
                      Image(systemName: "rectangle.inset.filled.and.person.filled")
                          .symbolEffect(.pulse)
                          .frame(maxWidth: .infinity)
                      Image(systemName: "arrow.down.circle")
                          .symbolEffect(.bounce, value: downloadCount)
                      Image(systemName: "wifi")
                          .symbolEffect(.variableColor.iterative.reversing)
                      Image(systemName: "bubble.left.and.bubble.right.fill")
                          .symbolEffect(.scale.up, isActive: scaleUpActive)
                      Image(systemName: "cloud.sun.rain.fill")
                          .symbolEffect(.disappear, isActive: isHidden)
                      Image(systemName: isPlaying ? "play.fill" : "pause.fill")
                          .contentTransition(.symbolEffect(.replace.downUp))
                  }
                  .padding()
              }
              .font(.system(size: 64))
              .frame(maxWidth: .infinity)
              .symbolRenderingMode(.multicolor)
              .preferredColorScheme(.dark)
              .task {
                  do {
                      while true {
                          try await Task.sleep(for: .milliseconds(1500))
                          if !isPaused {
                              downloadCount += 1
                          }
                      }
                  } catch {
                      print("exiting")
                  }
              }
          }
      }
      
      #Preview {
          SymbolEffect_Snippet()
      }
    • 25:35 - Metal Shader (cont.)

      import SwiftUI
      
      struct ShaderUse_Snippet: View {
          @State private var stripeSpacing: Float = 10.0
          @State private var stripeAngle: Float = 0.0
      
          var body: some View {
              VStack {
                  Text(
                      """
                      \(
                          Text("Furdinand")
                              .foregroundStyle(stripes)
                              .fontWidth(.expanded)
                      ) \
                      is a good dog!
                      """
                  )
                  .font(.system(size: 56, weight: .heavy).width(.condensed))
                  .lineLimit(...4)
                  .multilineTextAlignment(.center)
                  Spacer()
                  controls
                  Spacer()
              }
              .padding()
          }
      
          var stripes: Shader {
              ShaderLibrary.angledFill(
                  .float(stripeSpacing),
                  .float(stripeAngle),
                  .color(.blue)
              )
          }
      
          @ViewBuilder
          var controls: some View {
              Grid(alignment: .trailing) {
                  GridRow {
                      spacingSlider
                      ZStack(alignment: .trailing) {
                          Text("50.0 PX").hidden() // maintains size
                          Text("""
                              \(stripeSpacing,
                              format: .number.precision(.fractionLength(1))) \
                              \(Text("PX").textScale(.secondary))
                              """)
                              .foregroundStyle(.secondary)
                      }
                  }
                  GridRow {
                      angleSlider
                      ZStack(alignment: .trailing) {
                          Text("-0.09π RAD").hidden() // maintains size
                          Text("""
                              \(stripeAngle / .pi,
                              format: .number.precision(.fractionLength(2)))π \
                              \(Text("RAD").textScale(.secondary))
                              """)
                              .foregroundStyle(.secondary)
                      }
                  }
              }
              .labelsHidden()
          }
      
          @ViewBuilder
          var spacingSlider: some View {
              Slider(
                  value: $stripeSpacing, in: Float(10.0)...50.0) {
                      Text("Spacing")
                  } minimumValueLabel: {
                      Image(
                          systemName:
                              "arrow.down.forward.and.arrow.up.backward")
                  } maximumValueLabel: {
                      Image(
                          systemName:
                              "arrow.up.backward.and.arrow.down.forward")
                  }
          }
      
          @ViewBuilder
          var angleSlider: some View {
              Slider(
                  value: $stripeAngle, in: (-.pi / 2)...(.pi / 2)) {
                      Text("Angle")
                  } minimumValueLabel: {
                      Image(
                          systemName: "arrow.clockwise")
                  } maximumValueLabel: {
                      Image(
                          systemName: "arrow.counterclockwise")
                  }
          }
      }
      
      // NOTE: create a .metal file in your project and add the following to it:
      
      /*
      
      #include <metal_stdlib>
      using namespace metal;
      
      [[ stitchable ]] half4
      angledFill(float2 position, float width, float angle, half4 color)
      {
          float pMagnitude = sqrt(position.x * position.x + position.y * position.y);
          float pAngle = angle +
              (position.x == 0.0f ? (M_PI_F / 2.0f) : atan(position.y / position.x));
          float rotatedX = pMagnitude * cos(pAngle);
          float rotatedY = pMagnitude * sin(pAngle);
          return (color + color * fmod(abs(rotatedX + rotatedY), width) / width) / 2;
      }
      
       */
      
      #Preview {
          ShaderUse_Snippet()
      }
    • 26:11 - Typesetting Language

      import SwiftUI
      
      struct TypesettingLanguage_Snippet: View {
          var dog = Dog(
              name: "ไมโล",
              language: .init(languageCode: .thai),
              imageName: "Puppy_Pitbull")
      
          func phrase(for name: Text) -> Text {
              Text(
                  "Who's a good dog, \(name)?"
              )
          }
      
          var body: some View {
              HStack(spacing: 54) {
                  VStack {
                      phrase(for: Text("Milo"))
                  }
                  VStack {
                      phrase(for: Text(dog.name))
                  }
                  VStack {
                      phrase(for: dog.nameText)
                  }
              }
              .font(.title)
              .lineLimit(...5)
              .multilineTextAlignment(.leading)
              .padding()
          }
      
          struct Dog {
              var name: String
              var language: Locale.Language
              var imageName: String
      
              var nameText: Text {
                  Text(name).typesettingLanguage(language)
              }
          }
      }
      
      #Preview {
          TypesettingLanguage_Snippet()
      }
    • 27:46 - ScrollView Transitions And Behaviors

      import SwiftUI
      
      struct ScrollingRecentDogsView: View {
          private static let colors: [Color] = [.red, .blue, .brown, .yellow, .purple]
      
          private var dogs: [Dog] = (1..<10).map {
              .init(
                  name: "Dog \($0)", color: colors[Int.random(in: 0..<5)],
                  isFavorite: false)
          }
      
          private var parks: [Park] = (1..<10).map { .init(name: "Park \($0)") }
      
          @State private var scrolledID: Dog.ID?
      
          var body: some View {
              ScrollView {
                  LazyVStack {
                      ForEach(dogs) { dog in
                          DogCard(dog: dog, isTop: scrolledID == dog.id)
                              .scrollTransition { content, phase in
                                  content
                                      .scaleEffect(phase.isIdentity ? 1 : 0.8)
                                      .opacity(phase.isIdentity ? 1 : 0)
                              }
                      }
                  }
              }
              .scrollPosition(id: $scrolledID)
              .safeAreaInset(edge: .top) {
                  ScrollView(.horizontal) {
                      LazyHStack {
                          ForEach(parks) { park in
                              ParkCard(park: park)
                                  .aspectRatio(3.0 / 2.0, contentMode: .fill)
                                  .containerRelativeFrame(
                                      .horizontal, count: 5, span: 2, spacing: 8)
                          }
                      }
                      .scrollTargetLayout()
                  }
                  .scrollTargetBehavior(.viewAligned)
                  .padding(.vertical, 8)
                  .fixedSize(horizontal: false, vertical: true)
                  .background(.thinMaterial)
              }
              .safeAreaPadding(.horizontal, 16.0)
          }
          
          struct DogCard: View {
              var dog: Dog
              var isTop: Bool
      
              var body: some View {
                  DogImage(dog: dog)
                      .overlay(alignment: .bottom) {
                          HStack {
                              Text(dog.name)
                              Spacer()
                              if isTop {
                                  TopDog()
                              }
                              Spacer()
                              Image(systemName: "heart")
                                  .symbolVariant(dog.isFavorite ? .fill : .none)
                          }
                          .font(.headline)
                          .padding(.horizontal, 22)
                          .padding(.vertical, 12)
                          .background(.thinMaterial)
                      }
                      .clipShape(.rect(cornerRadius: 16))
              }
          }
      
          struct DogImage: View {
              var dog: Dog
      
              var body: some View {
                  Rectangle()
                      .fill(dog.color.gradient)
                      .frame(height: 400)
              }
          }
      
          struct TopDog: View {
              var body: some View {
                  HStack {
                      Image(systemName: "trophy.fill")
                      Text("Top Dog")
                      Image(systemName: "trophy.fill")
                  }
              }
          }
      
          struct ParkCard: View {
              var park: Park
      
              var body: some View {
                  RoundedRectangle(cornerRadius: 8)
                      .fill(.green.gradient)
                      .overlay {
                          Text(park.name)
                              .padding()
                      }
              }
          }
      
          struct Dog: Identifiable {
              var id = UUID()
              var name: String
              var color: Color
              var isFavorite: Bool
          }
      
          struct Park: Identifiable {
              var id = UUID()
              var name: String
          }
      }
    • 31:12 - Menu Enhancements

      import SwiftUI
      
      struct DogTagEditMenu: View {
          @State private var selectedColor = TagColor.blue
      
          var body: some View {
              Menu {
                  ControlGroup {
                      Button {
                      } label: {
                          Label("Cut", systemImage: "scissors")
                      }
                      Button {
                      } label: {
                          Label("Copy", systemImage: "doc.on.doc")
                      }
                      Button {
                      } label: {
                          Label("Paste", systemImage: "doc.on.clipboard.fill")
                      }
                      Button {
                      } label: {
                          Label("Duplicate", systemImage: "plus.square.on.square")
                      }
                  }
                  .controlGroupStyle(.compactMenu)
      
                  Picker("Tag Color", selection: $selectedColor) {
                      ForEach(TagColor.allCases) {
                          Label($0.rawValue.capitalized, systemImage: "tag")
                              .tint($0.color)
                              .tag($0)
                      }
                  }
                  .paletteSelectionEffect(.symbolVariant(.fill))
                  .pickerStyle(.palette)
              } label: {
                  Label("Edit", systemImage: "ellipsis.circle")
              }
              .menuStyle(.button)
          }
      
          enum TagColor: String, CaseIterable, Identifiable {
              case blue
              case brown
              case green
              case yellow
      
              var id: Self { self }
      
              var color: Color {
                  switch self {
                  case .blue: return .blue
                  case .brown: return .brown
                  case .green: return .green
                  case .yellow: return .yellow
                  }
              }
          }
      }
    • 32:30 - Highlight Hover Effect

      import SwiftUI
      
      struct DogGalleryCard: View {
          @FocusState private var isFocused: Bool
      
          var body: some View {
              Button {
              } label: {
                  VStack {
                      RoundedRectangle(cornerRadius: 8)
                          .fill(.blue)
                          .frame(width: 888, height: 500)
                          .hoverEffect(.highlight)
      
                      Text("Name")
                          .opacity(isFocused ? 1 : 0)
                  }
              }
              .buttonStyle(.borderless)
              .focused($isFocused)
          }
      }
  • 特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。

    クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。

Developer Footer

  • ビデオ
  • WWDC23
  • SwiftUIの新機能
  • メニューを開く メニューを閉じる
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    メニューを開く メニューを閉じる
    • アクセシビリティ
    • アクセサリ
    • App Extension
    • App Store
    • オーディオとビデオ(英語)
    • 拡張現実
    • デザイン
    • 配信
    • 教育
    • フォント(英語)
    • ゲーム
    • ヘルスケアとフィットネス
    • アプリ内課金
    • ローカリゼーション
    • マップと位置情報
    • 機械学習
    • オープンソース(英語)
    • セキュリティ
    • SafariとWeb(英語)
    メニューを開く メニューを閉じる
    • 英語ドキュメント(完全版)
    • 日本語ドキュメント(一部トピック)
    • チュートリアル
    • ダウンロード(英語)
    • フォーラム(英語)
    • ビデオ
    Open Menu Close Menu
    • サポートドキュメント
    • お問い合わせ
    • バグ報告
    • システム状況(英語)
    メニューを開く メニューを閉じる
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles(英語)
    • フィードバックアシスタント
    メニューを開く メニューを閉じる
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program(英語)
    • News Partner Program(英語)
    • Video Partner Program(英語)
    • セキュリティ報奨金プログラム(英語)
    • Security Research Device Program(英語)
    Open Menu Close Menu
    • Appleに相談
    • Apple Developer Center
    • App Store Awards(英語)
    • Apple Design Awards
    • Apple Developer Academy(英語)
    • WWDC
    Apple Developerアプリを入手する
    Copyright © 2025 Apple Inc. All rights reserved.
    利用規約 プライバシーポリシー 契約とガイドライン