ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
SwiftUI Appに複数のウインドウを追加する
Appのシーン内でウインドウを表示できる最新のSwiftUI APIをご覧ください。MenuBarExtraなどのシーンタイプにより、SwiftUIでさまざまな種類のAppが簡単に構築できるようになる仕組みについて解説します。また、修飾子を使用して、Appウインドウのプレゼンテーションや動作をカスタマイズし、macOS Appを向上させる方法も紹介します。
リソース
- Bringing multiple windows to your SwiftUI app
- DocumentGroup
- MenuBarExtra
- NewDocumentAction
- OpenDocumentAction
- OpenWindowAction
- Value and Reference Types
- Window
- WindowGroup
関連ビデオ
WWDC22
-
ダウンロード
♪ 落ち着いた ヒップホップの曲 ♪ ♪ みなさん こんにちは SwiftUIチームのエンジニア Jeffです 今日は iPadOSとmacOSで SwiftUIAppに 複数のウインドウを 表示する方法について 説明します このセッションでは SwiftUIライフサイクルの さまざまなシーンタイプの 概要を公開します これにはいくつかの 新しいタイプも含まれます 次に補助シーンを追加し シーンタイプを どのように組み合わせて 構成できるかを示します そして Appで 特定のシーンウインドウを 開くための 新しいAPIの説明をします 最後にAppのシーンを カスタマイズする いくつかの方法について まとめます では現在のシーンタイプの 概要から始めて 新しいシーンタイプについても 見てみましょう 以前のセッションで SwiftUIAppが シーンとビューで構成されている ことをお話ししました シーンは主にウインドウ画面上に コンテンツを表示します たとえば 私が読んでいる本の 記録を付けるために 構築したAppがあります 単一のウインドウグループと 定義され プラットフォームに合わせた適切な方法で 閲覧リスト表示されます iPadOSやmacOSなど複数の ウインドウをサポートする プラットフォームでは シーンは複数ウインドウで 表示されます シーンの動作と表現は 使用するタイプによって 異なります 例えばシーンは プラットフォームの機能に 関係なく単一の インスタンスでのみ 表現されます SwiftUIの現在の シーンタイプのリストを 見てみましょう WindowGroupはAppleの すべてのプラットフォームで データ駆動型Appを 構築する方法を提供します DocumentGroupでは iOSやmacOS上で ドキュメントベースの Appを構築できます SettingsではmacOSのAppの 設定値を表すため インターフェースを 定義します これらのシーンタイプは Appの機能を 拡張するために 組み合わせて構成できます 今回 2つの新しい機能を追加して シーンのリストを拡張します 1つ目は: Window すべてのプラットフォームで 1つの固有のウインドウを 表すものです またmacOSの新しい シーンタイプ:MenuBarExtra これはシステムメニューバーで 永続コントロールとして表示されます 他のシーンタイプと同様に WindowとMenuBarExtraを 独立型のシーンとして 使うことも App内の他のシーンと 組み合わせて 使うこともできます WindowGroupとは異なり Windowシーンは単一の ウインドウインスタンス内で のみ内容を表現します この特性は シーンのコンテンツが macOSおよびiPadOS上の WindowGroupsの マルチウインドウ形式に 適していない いくつかの グローバルApp状態のような 場合に役立ちます たとえば ゲームでは 1つのメインウインドウだけが コンテンツを表示するように したほうがいいでしょう MenuBarExtraは新しい macOSのシーンタイプで 他のシーンとは少し違う 動作をします ウインドウに内容を表示する のではなく メニューバーにラベルを表示し メニューまたはウインドウの 内容を見せて ラベルに固定されます さらに関連するAppが 実行中であれば Appが最フォアグラウンドにあるか どうかにかかわらず それを使用できます MenuBarExtraは機能に簡単に アクセスできる 独立型の実用的なAppを 開発するのに最適です または他のシーンと 組み合わせたAppの機能に アクセスするための 別の方法を提供もできます また2つの表示スタイルも サポートされています デフォルトで メニューバーから下に 表示するスタイルと メニューバーに固定された クロムレスウインドウに 表示するスタイルです これら2つの新しい シーンタイプが追加され SwiftUIAppは すべてのプラットフォームで より豊富な一連の機能を 表示します これらの新しいAPIを 既存のシーンタイプと 合わせて使う 方法を見てみましょう 先ほど示した BookClub Appの 定義です 現在はウインドウグループ 1つで構成されています macOSでBookClub Appを 使用すると 経時的に読書活動を表示する ための追加ウインドウが 役に立つ場合があります これはmacOSAppが プラットフォームにある 追加の画面領域と 現在の柔軟な ウインドウ配置を どのように利用できるかを 示すよい例です このインターフェースを 表す補助シーンを Appに追加します アクティビティウインドウの データは App全体の状態から 取得されるため ウインドウシーンが理想的な 選択肢となります 同じ状態で 複数のウインドウを開くと 設計にうまく適合しません シーンのタイトルは ウインドウメニューの セクションに追加される 項目のラベルに使われます このアイテムを選択すると シーンのウインドウが 開いていない場合は開き それ以外の場合は フォアグラウンドに表示されます BookClub Appに 補助シーンを追加でした 次に追加する新しい シーンの一部の プレゼンテーションテーションAPIと それらをAppに統合して より豊かな体験を 提供する方法について 説明します BookClub App コンテンツリストペインの 本に対して呼び出せる コンテキストメニューがあり これにはウインドウ プレゼンテーションを 動かすボタンが含まれます 詳細は後ほど説明します SwiftUIはAppで 定義されているシーンに 関連付けられたウインドウを 表示するための環境を通して 複数の新しい呼び出し可能な タイプを提供します 1つ目は openWindowアクションで WindowGroupまたは ウインドウシーンの いずれかのウインドウを 表示できます アクションに渡される 識別子はAppで定義された シーンの識別子と 一致する必要があります openWindowアクションでは プレゼンテーション値を取ることもでき プレゼンテーションシーンで内容を 表示するために使用されます この形式はWindowGroupで のみサポートされており 新しい初期化子を使用します この方法を見ていきます 値の型はシーンの初期化子に 指定された型と対象に 一致する必要があり ドキュメントウインドウを 表示する環境には 呼び出し可能な2つがあります newDocumentアクションは FileDocumentsと ReferenceFileDocuments 両方のウインドウを 開くサポートをします このアクションはApp内で 対応するDocumentGroupが エディターの役割で 定義される必要があります このアクションに 提供されるドキュメントは ウインドウが表示されるたび 作成されます ディスク上の既存のファイル からコンテンツが提供され ドキュメントウインドウを 表示するには OpenDocumentアクションが あります これは開くファイルのURLを 取得します Appでウインドウを 表示するための DocumentGroupを 定義する必要があり グループのドキュメント タイプでは指定されたURLで ファイルのタイプを 読み取る必要があります ボタンを再考すると openWindow環境プロパティ がビューに追加されます このタイプは呼び出し可能で ボタンの操作から 直接呼び出すことができます 当社のブックタイプは 識別に準拠しています そのため識別子を示す値 として渡します openWindowアクションに 渡される値について 説明します 私はブックの識別子が UUIDタイプの値を 渡していることに 注目しました 一般的には値自体ではなく モデルの識別子を このように使いたいでしょう ブックタイプは値型である ことに注目してください そのため現在値として 使用する場合 新しいウインドウには プレゼンテーションを由来の ウインドウのコピーが 表示されます どちらか片方を編集しても もう片方には影響しません ブックの識別子を使うと モデルを保存できます 代わりにこれらの値の 真実のもととなります 単一の値に複数の結合を 提供します 値型のセマンティクスの 詳細については デベロッパ向けマニュアルを 参照してください 表示されるタイプは ハッシュ可能とコード可能な プロトコルの両方に 適合している必要があります ウインドウを開き提示された 値を関連付けるには ハッシュ可能な 適合性が必要です ただし状態復元に 提示された値を 維持する適合性の確認は 必要です この2つの行動の 詳細については 後ほど説明します 最後に可能であれば軽量値を 渡すことをお勧めします ブックの識別子は もう1つの大きい例である値は 状態復旧のSwiftUIで 持続すためには 小さい値を使用します Appの応答性向上に つながります 詳細ウインドウを 表示するのに 必要なボタンが 追加されましたが 選択しても 何も表示されません これはSwiftUIに特定の データ型のウインドウを 表示するように 指示したためですが これを反映したシーンを Appで定義していません Appに戻って変更を 加えてみましょう Appに戻って変更を 加えてみましょう 一緒にブックの詳細を 取り扱うために さらなるWindowGroup を追加しました ブックではWindowGroupで 新しい初期化子を使います タイトルに加えて グループはBook.IDタイプの データを表示しています この場合はUUIDです これは先ほど追加した openWindowアクションに 渡す値と一致する 必要があります プレゼンテーション用に WindowGroupに値を 指定すると SwiftUIはその値の新しい 子シーンを作成し そのシーンのウインドウの ルートコンテンツは グループのview builderを 使ってその値で定義されます 固有の提示された値によって 新しいシーンが作成されます 値の等価性は 新しいウインドウを作るか 既存のウインドウを 再利用できるかを 決定するために使われます ウインドウが存在するため openWindowに 値が表示される場合 グループは 新しいウインドウを作成せず そのウインドウを使います 例としてBookClub Appを 使います コンテキストメニュー アクションを選択します これはすでにウインドウに 表示され ウインドウはフォアグラウンドに 並べられます 同じブックを表示する2つ目の ウインドウではありません 提示された値も 状態復旧の目的で SwiftUIによって自動的に 永続化されます ビューは初期値に 結合が与えられます この結合はウインドウが 開いている間 いつでも変更できます シーンが状態復旧のために 再作成されると SwiftUIは最新の値を ウインドウの コンテンツビューに渡します ここではBook.IDを 詳細ビューに結合します モデルストアに表示され 指定した項目を検索できます すべてを配置し コンテキストメニュー項目を 選択できるようになり また独自のウインドウで ブックの詳細を表示できます 最後にAppのシーンを カスタマイズする いくつかの方法を説明します 2つのWindowGroupシーンで Appを定義し 1つはメインビューアー ウインドウで そして1つは 詳細ウインドウです デフォルトではSwiftUIで ファイルメニューの 各グループにメニュー項目が 追加されますが 詳細ウインドウの メニュー項目は 私たちの使用事例には 適していません 前に追加した コンテキストメニューにより ウインドウは開けておく ほうが良いと思います 新しいシーン修飾子である コマンドが削除され シーンを修正でき デフォルトのコマンドが 提供されなくなります ファイルメニューと同様です この修飾子を適用すると プライマリWindowGroupの ウインドウを開く項目が メニューにのみ表示されます 読書活動を表示するために 補助ウインドウシーンの 現在のプレゼンテーションには 満足していません 次にその点に 注目してみましょう 修飾子をいくつか適用し 次のようになります それをカスタムシーンに 抽出し App定義が クリーンになります ウインドウの 前の状態はなく SwiftUIはデフォルトで 画面の中央に配置されます 読書活動はデフォルトでは 別の場所に配置される方が いいですね 新しいdefaultPosition修飾子 を追加し 前の状態が使えない場合 使う場所を指定できます この位置は 画面サイズに対して Appの場所にウインドウを 配置し 現在のロケールを考慮します この新しい位置は画面上の 他の表示ウインドウから アクティビティウインドウを 区別するのに役立ちます アクティビティウインドウも デフォルトでは 特定のサイズで表示されます がサイズ変更は可能です defaultPositionの横に defaultSize修飾子を 追加します 値はレイアウトシステムに 与えられ ウインドウの初期サイズを 取得します ウインドウのプレゼンテーションを カスタマイズし 1つ修飾子を追加して動作を カスタマイズしましょう keyboardShortcut修飾子が 展開され その上シーンタイプでも 作用します シーンレベルで使う場合は 新しいウインドウが作成され この修飾子はコマンドに 影響します ショートカット option+ command+0を開いてできる アクティビティウインドウを 変更しました これはAppをカスタマイズ するのに適した方法で 一般的に使われるシーンへの ショートカットを提供し command+Nのデフォルトの ショートカットを カスタマイズするためにも 使え Appの基本WindowGroupに 追加されます SwiftUIの新しい シーンとウインドウ機能の ツアーはこれで終わりです 新しいAPIの可能性には 本当に興奮していて あなたにとっても そうあることを願っています iPadOSやmacOSAppに 機能を追加する方法の詳細は 「iPadのSwiftUI:インター フェースの構造化」 「iPadのSwiftUI:インター フェースの整理」 「iPadのSwiftUI:ツールバー やタイトルなどを追加」 のセッションをご覧ください
ご覧いただき ありがとうございました ♪
-
-
2:01 - Scene composition
import SwiftUI import UniformTypeIdentifiers @main struct MultiSceneApp: App { var body: some Scene { WindowGroup { ContentView() } #if os(iOS) || os(macOS) DocumentGroup(viewing: CustomImageDocument.self) { file in ImageViewer(file.document) } #endif #if os(macOS) Settings { SettingsView() } #endif } } struct ContentView: View { var body: some View { Text("Content") } } struct ImageViewer: View { var document: CustomImageDocument init(_ document: CustomImageDocument) { self.document = document } var body: some View { Text("Image") } } struct SettingsView: View { var body: some View { Text("Settings") } } struct CustomImageDocument: FileDocument { var data: Data static var readableContentTypes: [UTType] { [UTType.image] } init(configuration: ReadConfiguration) throws { guard let data = configuration.file.regularFileContents else { throw CocoaError(.fileReadCorruptFile) } self.data = data } func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper { FileWrapper(regularFileWithContents: data) } }
-
2:34 - Adding a window scene
import SwiftUI @main struct BookClub: App { @StateObject private var store = ReadingListStore() var body: some Scene { WindowGroup { ReadingListViewer(store: store) } Window("Activity", id: "activity") { ReadingActivity(store: store) } } } struct ReadingListViewer: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading List") } } struct ReadingActivity: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading Activity") } } class ReadingListStore: ObservableObject { }
-
3:01 - Standalone menu bar extra app
import SwiftUI @main struct UtilityApp: App { var body: some Scene { MenuBarExtra("Utility App", systemImage: "hammer") { AppMenu() } } } struct AppMenu: View { var body: some View { Text("App Menu Item") } }
-
3:35 - Windowed app with menu bar extra
import SwiftUI @main struct BookClub: App { @StateObject private var store = ReadingListStore() var body: some Scene { WindowGroup { ReadingListViewer(store: store) } #if os(macOS) MenuBarExtra("Book Club", systemImage: "book") { AppMenu() } #endif } } struct ReadingListViewer: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading List") } } struct AppMenu: View { var body: some View { Text("App Menu Item") } } class ReadingListStore: ObservableObject { }
-
3:42 - Menu bar extra with default style
import SwiftUI @main struct UtilityApp: App { var body: some Scene { MenuBarExtra("Utility App", systemImage: "hammer") { AppMenu() } } } struct AppMenu: View { var body: some View { Text("App Menu Item") } }
-
3:49 - Menu bar extra with window style
import SwiftUI @main struct UtilityApp: App { var body: some Scene { MenuBarExtra("Time Tracker", systemImage: "rectangle.stack.fill") { TimeTrackerChart() } .menuBarExtraStyle(.window) } } struct TimeTrackerChart: View { var body: some View { Text("Time Tracker Chart") } }
-
4:14 - Book Club app definition
import SwiftUI @main struct BookClubApp: App { @StateObject private var store = ReadingListStore() var body: some Scene { WindowGroup { ReadingListViewer(store: store) } } } struct ReadingListViewer: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading List") } } class ReadingListStore: ObservableObject { }
-
4:38 - Adding an auxiliary Window Scene
import SwiftUI @main struct BookClub: App { @StateObject private var store = ReadingListStore() var body: some Scene { WindowGroup { ReadingListViewer(store: store) } Window("Activity", id: "activity") { ReadingActivity(store: store) } } } struct ReadingListViewer: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading List") } } struct ReadingActivity: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading Activity") } } class ReadingListStore: ObservableObject { }
-
5:28 - Open book context menu button
import SwiftUI struct OpenBookButton: View { var book: Book var body: some View { Button("Open In New Window") { } } } struct Book: Identifiable { var id: UUID }
-
5:34 - Opening a window using an identifier
import SwiftUI @main struct BookClub: App { @StateObject private var store = ReadingListStore() var body: some Scene { WindowGroup { ReadingListViewer(store: store) } Window("Activity", id: "activity") { ReadingActivity(store: store) } } } struct OpenWindowButton: View { @Environment(\.openWindow) private var openWindow var body: some View { Button("Open Activity Window") { openWindow(id: "activity") } } } struct ReadingListViewer: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading List") } } struct ReadingActivity: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading Activity") } } class ReadingListStore: ObservableObject { }
-
5:57 - Opening a window using a presented value
import SwiftUI @main struct BookClub: App { @StateObject private var store = ReadingListStore() var body: some Scene { WindowGroup { ReadingListViewer(store: store) } Window("Activity", id: "activity") { ReadingActivity(store: store) } WindowGroup("Book Details", for: Book.ID.self) { $bookId in BookDetail(id: $bookId, store: store) } } } struct OpenWindowButton: View { var book: Book @Environment(\.openWindow) private var openWindow var body: some View { Button("Open In New Window") { openWindow(value: book.id) } } } struct ReadingListViewer: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading List") } } struct ReadingActivity: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading Activity") } } struct BookDetail: View { @Binding var id: Book.ID? @ObservedObject var store: ReadingListStore var body: some View { Text("Book Details") } } struct Book: Identifiable { var id: UUID } class ReadingListStore: ObservableObject { }
-
6:16 - Opening a window with a new document
import SwiftUI import UniformTypeIdentifiers @main struct TextFileApp: App { var body: some Scene { DocumentGroup(viewing: TextFile.self) { file in TextEditor(text: file.$document.text) } } } struct NewDocumentButton: View { @Environment(\.newDocument) private var newDocument var body: some View { Button("Open New Document") { newDocument(TextFile()) } } } struct TextFile: FileDocument { var text: String static var readableContentTypes: [UTType] { [UTType.plainText] } init() { text = "" } init(configuration: ReadConfiguration) throws { guard let data = configuration.file.regularFileContents, let string = String(data: data, encoding: .utf8) else { throw CocoaError(.fileReadCorruptFile) } text = string } func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper { let data = text.data(using: .utf8)! return FileWrapper(regularFileWithContents: data) } }
-
6:41 - Opening a window with an existing document
import SwiftUI import UniformTypeIdentifiers @main struct TextFileApp: App { var body: some Scene { DocumentGroup(viewing: TextFile.self) { file in TextEditor(text: file.$document.text) } } } struct OpenDocumentButton: View { var documentURL: URL @Environment(\.openDocument) private var openDocument var body: some View { Button("Open Document") { Task { do { try await openDocument(at: documentURL) } catch { // Handle error } } } } } struct TextFile: FileDocument { var text: String static var readableContentTypes: [UTType] { [UTType.plainText] } init() { text = "" } init(configuration: ReadConfiguration) throws { guard let data = configuration.file.regularFileContents, let string = String(data: data, encoding: .utf8) else { throw CocoaError(.fileReadCorruptFile) } text = string } func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper { let data = text.data(using: .utf8)! return FileWrapper(regularFileWithContents: data) } }
-
7:03 - Book details context menu button
struct OpenWindowButton: View { var book: Book @Environment(\.openWindow) private var openWindow var body: some View { Button("Open In New Window") { openWindow(value: book.id) } } } struct Book: Identifiable { var id: UUID }
-
7:08 - Book details context menu button
struct OpenWindowButton: View { var book: Book @Environment(\.openWindow) private var openWindow var body: some View { Button("Open In New Window") { openWindow(value: book.id) } } } struct Book: Identifiable { var id: UUID }
-
9:06 - Book Club app with book details Scene
import SwiftUI @main struct BookClub: App { @StateObject private var store = ReadingListStore() var body: some Scene { WindowGroup { ReadingListViewer(store: store) } Window("Activity", id: "activity") { ReadingActivity(store: store) } WindowGroup("Book Details", for: Book.ID.self) { $bookId in BookDetail(id: $bookId, store: store) } } } struct ReadingListViewer: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading List") } } struct ReadingActivity: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading Activity") } } struct BookDetail: View { @Binding var id: Book.ID? @ObservedObject var store: ReadingListStore var body: some View { Text("Book Details") } } struct Book: Identifiable { var id: UUID } class ReadingListStore: ObservableObject { }
-
10:32 - Book Club app with book details Scene
import SwiftUI @main struct BookClub: App { @StateObject private var store = ReadingListStore() var body: some Scene { WindowGroup { ReadingListViewer(store: store) } Window("Activity", id: "activity") { ReadingActivity(store: store) } WindowGroup("Book Details", for: Book.ID.self) { $bookId in BookDetail(id: $bookId, store: store) } } } struct ReadingListViewer: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading List") } } struct ReadingActivity: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading Activity") } } struct BookDetail: View { @Binding var id: Book.ID? @ObservedObject var store: ReadingListStore var body: some View { Text("Book Details") } } struct Book: Identifiable { var id: UUID } class ReadingListStore: ObservableObject { }
-
11:16 - Removing default commands for the book details scene
import SwiftUI @main struct BookClub: App { @StateObject private var store = ReadingListStore() var body: some Scene { WindowGroup { ReadingListViewer(store: store) } Window("Activity", id: "activity") { ReadingActivity(store: store) } WindowGroup("Book Details", for: Book.ID.self) { $bookId in BookDetail(id: $bookId, store: store) } .commandsRemoved() } } struct ReadingListViewer: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading List") } } struct ReadingActivity: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading Activity") } } struct BookDetail: View { @Binding var id: Book.ID? @ObservedObject var store: ReadingListStore var body: some View { Text("Book Details") } } struct Book: Identifiable { var id: UUID } class ReadingListStore: ObservableObject { }
-
11:46 - Extracting reading activity into custom scene
import SwiftUI @main struct BookClub: App { @StateObject private var store = ReadingListStore() var body: some Scene { WindowGroup { ReadingListViewer(store: store) } ReadingActivityScene(store: store) WindowGroup("Book Details", for: Book.ID.self) { $bookId in BookDetail(id: $bookId, store: store) } .commandsRemoved() } } struct ReadingActivityScene: Scene { @ObservedObject var store: ReadingListStore var body: some Scene { Window("Activity", id: "activity") { ReadingActivity(store: store) } } } struct ReadingListViewer: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading List") } } struct ReadingActivity: View { @ObservedObject var store: ReadingListStore var body: some View { Text("Reading Activity") } } struct BookDetail: View { @Binding var id: Book.ID? @ObservedObject var store: ReadingListStore var body: some View { Text("Book Details") } } struct Book: Identifiable { var id: UUID } class ReadingListStore: ObservableObject { }
-
12:04 - Applying the defaultPosition modifier
struct ReadingActivityScene: Scene { @ObservedObject var store: ReadingListStore var body: some Scene { Window("Activity", id: "activity") { ReadingActivity(store: store) } .defaultPosition(.topTrailing) } } class ReadingListStore: ObservableObject { }
-
12:32 - Applying the defaultSize modifier
struct ReadingActivityScene: Scene { @ObservedObject var store: ReadingListStore var body: some Scene { Window("Activity", id: "activity") { ReadingActivity(store: store) } #if os(macOS) .defaultPosition(.topTrailing) .defaultSize(width: 400, height: 800) #endif } } class ReadingListStore: ObservableObject { }
-
12:50 - Applying the keyboardShortcut modifier
struct ReadingActivityScene: Scene { @ObservedObject var store: ReadingListStore var body: some Scene { Window("Activity", id: "activity") { ReadingActivity(store: store) } #if os(macOS) .defaultPosition(.topTrailing) .defaultSize(width: 400, height: 800) #endif #if os(macOS) || os(iOS) .keyboardShortcut("0", modifiers: [.option, .command]) #endif } } class ReadingListStore: ObservableObject { }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。