-
Modernize seu app do AppKit
Atualize seu app do AppKit com as convenções modernas do macOS. Aprofunde-se no tratamento de entradas com eventos de controle e reconhecedores de gestos, indo além dos ciclos de rastreamento tradicionais. Aprimore a navegação por teclado no seu app, implemente uma restauração de estado robusta após reinicializações e aproveite as novas APIs de concentricidade de cantos que permitem integrar sua interface perfeitamente à estética do macOS.
Capítulos
- 0:00 - Introdução
- 1:06 - Entrada moderna
- 1:27 - Manipulação moderna de eventos com reconhecedores de gestos
- 2:25 - Seleção, menus de contexto e arrastar e soltar
- 3:52 - Seleção de texto em visualizações personalizadas
- 4:26 - Eventos de controle e reconhecedores de gestos
- 5:51 - Navegação por Teclado e Itens de Status
- 8:57 - Continuidade entre aberturas
- 9:08 - Encerramento elegante do app
- 9:55 - Restauração de estado
- 14:09 - Atualizações de Design
- 14:24 - Atualizações do Liquid Glass no macOS 27
- 15:41 - Concentricidade
- 16:59 - Próximas etapas
Recursos
- Use SwiftUI with AppKit
- Restoring your app’s state with AppKit
- Gestures
- TN3212: Adopting gesture recognizers for Sidecar touch support
- NSControl.Events
Vídeos relacionados
WWDC26
-
Buscar neste vídeo...
-
-
3:35 - Modern dragging delegate
// Modern dragging delegate methods func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> (any NSPasteboardWriting)? { let pasteboardItem = NSPasteboardItem() pasteboardItem.setString(..., forType: .string) return pasteboardItem } -
4:55 - Control events
// Use control events let button = NSButton() button.addTarget( self, action: #selector(trackingEndedOutsideHandler), for: .trackingEndedOutside ) -
5:44 - hitTest override
override func hitTest(_ point: NSPoint) -> NSView? { return nil } -
6:24 - autorecalculatesKeyViewLoop
window.autorecalculatesKeyViewLoop = true -
7:37 - Expanded interface delegate — setup
// Set the expanded interface delegate @main class LightAppDelegate: NSObject, NSApplicationDelegate { lazy var lightStatusItem: NSStatusItem = { ... }() func applicationDidFinishLaunching(_ notification: Notification) { // ... lightStatusItem.expandedInterfaceDelegate = self } } -
7:52 - Expanded interface delegate — methods
// Implement the delegate methods extension LightAppDelegate: NSStatusItemExpandedInterfaceDelegate { // ... func statusItem(_ statusItem: NSStatusItem, didBegin session: NSStatusItemExpandedInterfaceSession) { // Show window } func statusItemDidEndExpandedInterfaceSession( _ statusItem: NSStatusItem, animated: Bool) { // Hide window } func selectedAction() { // Take the action // Cancel session to request window dismissal lightStatusItem.expandedInterfaceSession?.cancel() } } -
8:16 - Expanded interface delegate — cancel
// Cancel the session when dismissing extension LightAppDelegate: NSStatusItemExpandedInterfaceDelegate { // ... func statusItem(_ statusItem: NSStatusItem, didBegin session: NSStatusItemExpandedInterfaceSession) { // Show window } func statusItemDidEndExpandedInterfaceSession( _ statusItem: NSStatusItem, animated: Bool) { // Hide window } func selectedAction() { // Take the action // Cancel session to request window dismissal lightStatusItem.expandedInterfaceSession?.cancel() } } -
9:45 - preventsApplicationTerminationWhenModal
window.preventsApplicationTerminationWhenModal = false -
10:18 - Set window identifiers for state restoration
// Set window identifiers for state restoration @MainActor class MainWindowController: NSWindowController, NSWindowDelegate { // ... convenience init() { let window = NSWindow( ... ) // ... window.identifier = NSUserInterfaceItemIdentifier(WindowIdentifiers.mainWindow) window.setFrameAutosaveName(WindowIdentifiers.mainWindow) window.isRestorable = true window.restorationClass = WindowRestorationHandler.self // ... } } -
11:04 - encodeRestorableState
// Preserve state to recreate the UI @MainActor class MainWindowController: NSWindowController, NSWindowDelegate { // ... override func encodeRestorableState(with coder: NSCoder) { super.encodeRestorableState(with: coder) // ... coder.encode(selectedProduct?.identifier.uuid, forKey: RestorationKeys.productIdentifier) // ... } // ... } -
11:50 - invalidateRestorableState
// Invalidate restorable state when the view hierarchy changes @MainActor class MainWindowController: NSWindowController, NSWindowDelegate { // ... convenience init() { // ... splitViewController.onProductSelected = { [weak self] product in self?.invalidateRestorableState() } } } -
12:26 - restoreWindow(withIdentifier:)
// Restore windows class WindowRestorationHandler: NSObject, NSWindowRestoration { static func restoreWindow( withIdentifier identifier: NSUserInterfaceItemIdentifier, state: NSCoder, completionHandler: @escaping (NSWindow?, Error?) -> Void ) { //... if identifier == .mainWindow, let window = appDelegate.mainWindowController?.window { completionHandler(window, nil) } else if identifier == .imageWindow { let controller = ImageWindowController() appDelegate.imageWindowControllers.append(controller) completionHandler(controller.window, nil) } else { completionHandler(nil, error) } } } -
13:29 - restoreState
// Restore window UI @MainActor class MainWindowController: NSWindowController, NSWindowDelegate { //... override func restoreState(with coder: NSCoder) { super.restoreState(with: coder) if let productId = coder.decodeObject( of: [NSString.self], forKey: RestorationKeys.productIdentifier) as? String { splitViewController?.selectedProductId = productId } //... } } -
16:11 - cornerConfiguration
// Subclass NSView to override cornerConfiguration class LocalWeatherView: NSView { // ... override var cornerConfiguration: NSViewCornerConfiguration? { let radius: NSViewCornerRadius = .containerConcentric(minimumCornerRadius) return .uniformCorners(radius: radius) } // ... }
-