-
Use SwiftUI with AppKit
Discover how the Shortcuts app uses both SwiftUI and AppKit to create a top-tier experience on macOS. Follow along with the Shortcuts team as we explore how you can host SwiftUI views in AppKit code, handle layout and sizing, participate in the responder chain, enable navigational focus, and more. We'll also show you how to host AppKit views, helping you migrate existing code into a SwiftUI layout within your app.
Recursos
Vídeos relacionados
WWDC22
WWDC21
-
Buscar neste vídeo...
-
-
1:29 - SidebarView and SidebarItem
struct SidebarView: View { @State private var selectedItem: SidebarItem var body: some View { List(selection: $selectedItem) { ... Section("Shortcuts") { ... } Section("Folders") { ... } } } } enum SidebarItem: Hashable { case gallery case allShortcuts ... case folder(Folder) } -
1:53 - Hosting SwiftUI sidebar
let splitViewController = NSSplitViewController() let sidebar = NSHostingController(rootView: SidebarView(...)) let splitViewItem = NSSplitViewItem(viewController: sidebar) splitViewController.addSplitViewItem(splitViewItem) -
3:06 - Sidebar selection model
class SelectionModel: ObservableObject { @Published var selectedItem: SidebarItem = .allShortcuts } // AppKit Window Controller cancellable = selectionModel.$selectedItem.sink { newItem in // update the NSSplitViewController detail } -
4:37 - Collection view item hosting SwiftUI
class ShortcutItemView: NSCollectionViewItem { private var hostingView: NSHostingView<ShortcutView>? func displayShortcut(_ shortcut: Shortcut) { let shortcutView = ShortcutView(shortcut: shortcut) if let hostingView = hostingView { hostingView.rootView = shortcutView } else { let newHostingView = NSHostingView(rootView: shortcutView) view.addSubview(newHostingView) setupConstraints(for: newHostingView) self.hostingView = newHostingView } } } -
7:55 - Popover presentation
viewController.present(NSHostingController(rootView: ...), asPopoverRelativeTo: rect, of: view, preferredEdge: .maxY, behavior: .transient) -
8:15 - Sheet presentation
viewController.presentAsSheet(NSHostingController(rootView: ...)) -
8:22 - Modal window presentation
let hostingController = NSHostingController(rootView: ModalView()) hostingController.title = "Window Title" viewController.presentAsModalWindow(hostingController) -
8:45 - Sizing options
hostingController.sizingOptions = [.minSize, .intrinsicContentSize, .maxSize] -
10:47 - Copy, Cut, and Paste commands
Image(...) .focusable() .copyable { ... } .cuttable { ... } .pasteDestination(payloadType: Image.self) { ... } -
11:02 - Respond to standard commands
struct ShortcutsEditorView: View { var body: some View { ScrollView { ... } .onMoveCommand { moveSelection(direction: $0) } .onExitCommand { cancelOperations() } .onCommand(#selector(NSResponder.selectAll(_:)) { selectAllActions() } .onCommand(#selector(moveActionUp(_:)) { moveSelectedAction(.up) } .onCommand(#selector(moveActionDown(_:)) { moveSelectedAction(.down) } } } -
15:18 - Script editor
class ScriptEditorView: NSView { var sourceCode: String var isEditable: Bool weak var delegate: ScriptEditorViewDelegate? } protocol ScriptEditorViewDelegate: AnyObject { func sourceCodeDidChange(in view: ScriptEditorView) -> Void } -
15:40 - Script editor container
struct ScriptEditorContainerView: View { @State var sourceCode: String = "" var body: some View { VStack { CompileButton { compile(code: sourceCode) } Divider() ScriptEditorRepresentable(sourceCode: $sourceCode) } } } -
16:13 - Script editor representable
struct ScriptEditorRepresentable: NSViewRepresentable { @Binding var sourceCode: String func makeNSView(context: Context) -> ScriptEditorView { let scriptEditor = ScriptEditorView(frame: .zero) scriptEditor.delegate = context.coordinator return scriptEditor } func updateNSView(_ nsView: ScriptEditorView, context: Context) { if sourceCode != scriptEditor.sourceCode { scriptEditor.sourceCode = sourceCode } scriptEditor.isEditable = context.environment.isEnabled // Make sure coordinator has a reference to the current value // of the binding: context.coordinator.representable = self } func makeCoordinator() -> Coordinator { Coordinator(representable: self) } } class Coordinator: NSObject, ScriptEditorViewDelegate { var representable: ScriptEditorRepresentable init(representable: ScriptEditorRepresentable) { ... } func sourceCodeDidChange(in view: ScriptEditorView) { representable.sourceCode = view.sourceCode } }
-