Touch-on-Mac-Doku: Migrationsguide, Spiele-Technote, HIG
Was hat Apple nach den Talks für Touch on Mac veröffentlicht? Am Abend des 28. Oktober war der AppKit-Artikel „Adopting gesture recognizers in your app“, der um 12 Uhr mittags noch 404 zurückgab, online, mit einer Tabelle aus vier Zeilen, die jeder Art von Code zur Mausverarbeitung einen nächsten Schritt zuweist; ein zweiter Artikel, „Handling AppKit gestures“, dokumentiert exklusive Views, ein Timeout von zwei Sekunden für hängende Recognizer und zwei Defaults zum Debuggen; eine neue Technote, TN3216, behandelt Spiele und die Reihenfolge emulierter Maus-Events; und drei Seiten der Human Interface Guidelines tragen auf den 28. Oktober datierte Einträge im Änderungsprotokoll zu Touch auf dem Mac.1234 Die Kachel „Documentation resources“ der Landingpage verlinkt jetzt den Artikel und das Beispiel zu File Promises, ihre Kachel „Design resources“ die drei HIG-Seiten, und die Kachel zur Xcode-27.3-Beta zeigt weiterhin „Coming soon“.5
Kurzfassung: Der Entwicklerbeitrag, veröffentlicht am 28. Oktober um 1:47 p.m. PT, stützte sich auf drei Tech Talks und eine Technote vom Juni; der AppKit-Artikel, den er gebraucht hätte, gab um 12 Uhr mittags 404 zurück. Um 9:51 p.m. war dieser Artikel online, mit zwei weiteren Dokumenten daneben. Der Migrationsartikel sortiert Ihren Code in vier Zeilen: Standard-Bedienelemente und Recognizer brauchen nichts; Overrides von mouseDown(with:) und Tracking-Schleifen erhalten eine Ein-Finger-Emulation und sollten zu Recognizern werden; eine Control-Unterklasse, die eine Mausmethode überschreibt, fällt auf eine Tracking-Schleife zurück; ein eigener Recognizer, der nie zum Abschluss kommt, hält seine exklusive View fest.1 Die Regel hinter der dritten Zeile lautet, dass AppKit danach entscheidet, ob die Methode überschrieben ist, „not on what the override does“ (nicht danach, was der Override tut), sodass ein Aufruf von super nicht hilft.1 Ein reiner Touch-NSPanGestureRecognizer mit buttonMask 0 neben einer Schleife, die Sie behalten, ist jetzt dokumentiert, samt der genauen Folge, wenn Sie die Maske unverändert lassen: Ein Ziehen mit der Maus startet den Recognizer, und Ihr mouseDown(with:) läuft nie.1 Exklusive Views sind standardmäßig aktiv, wenn der Info.plist-Schlüssel fehlt, und Xcodes Template macOS App schreibt den Schlüssel nur dann als NO, wenn der App-Lebenszyklus des Projekts der AppKit-App-Delegate ist, nicht beim standardmäßigen SwiftUI-Lebenszyklus; das habe ich in den Templates in Xcode 27.1 und der 27.2-Beta auf diesem Mac gelesen.27 Ein hängender Recognizer wird zwei Sekunden nach dem Ende der Interaktion abgebrochen, und beim Debuggen lässt ein User Default die App bei einem fehlenden Override abstürzen, während ein anderer den stillen Abbruch durch einen Absturz ersetzt.2 Die Technote für Spiele führt die genauen Maus-Events auf, die ein Tippen und ein Ziehen erzeugen, rät Engines davon ab, Deltas aufzusummieren, und erklärt, dass jede Touch-Oberfläche als zusätzliche emulierte GCMouse erscheint, bis NSIsTouchNative gesetzt ist.3 Die HIG nennt Touch „a secondary input mode“ (einen sekundären Eingabemodus), verlangt, die „tap targets bigger, not the controls themselves“ (Tap-Ziele größer, nicht die Bedienelemente selbst) zu machen, und ergänzt sechs Regeln für Bedienelemente, die bisher nur beim Hover erschienen.4 Weiterhin nicht da: die Xcode-27.3-Beta und die drei Schalter für Systemgesten, die um 9:51 p.m. PT 404 zurückgeben.56
Was erschienen ist, und wann
Jeder Punkt unten trägt die Uhrzeit, zu der ich ihn geprüft habe, denn die eigenen Stempel des Servers widersprechen dem, was die Seiten im Lauf des Tages zeigten; der Live-Beitrag dokumentiert den Tag, mit einer späten Zeile für diese Veröffentlichung.
- Der AppKit-Artikel. „Adopting gesture recognizers in your app“ gab um 12 Uhr mittags PT am Dokumentations-Endpunkt von Apple 404 zurück; um 9:51 p.m. war er online, ausgeliefert mit einem Änderungsstempel von 2:30 p.m. PT, aufgeführt im Abschnitt Essentials von AppKit nach „Adopting Liquid Glass“ und unter „Gesture handling“ auf der Gestures-Seite des Frameworks, und die erste Erfassung der Wayback Machine stammt von 6:08 p.m. PT (Belege in den Quellen).1 Die Tech-Talk-Seite von „Bring Touch to your Mac app“ verlinkt ihn weiterhin nicht.5
- Ein zweiter Artikel. „Handling AppKit gestures“ steht daneben auf der Gestures-Seite. Er hat keinen Revisionsverlauf, die Technote vom Juni verlinkt ihn nicht, die Wayback Machine hat keine Erfassung davon, und sein Serverstempel lautet 10:35 p.m. PT am 27. Oktober; er gehörte nicht zu den Seiten, die ich um 12 Uhr mittags geprüft habe, daher kann ich nicht sagen, wann er online ging.2
- Eine neue Technote. „TN3216: Supporting touch input for games on macOS“ hat einen einzeiligen Revisionsverlauf, „2026-10-28 First published“, und einen Serverstempel von 2:09 p.m. PT.3
- Drei HIG-Seiten. „Designing for macOS“, „Gestures“ und „Pointing devices“ enden jetzt mit Zeilen im Änderungsprotokoll, die auf den 28. Oktober 2026 datiert sind. Die Fassungen, die ich um 12 Uhr mittags abgerufen habe, enthielten nichts vom neuen Text; die um 9:51 p.m. abgerufenen enthalten ihn. Der Änderungs-Header des Servers auf der neuen Seite „Gestures“ lautet 5:11 UTC am 27. Oktober, also früher als die Mittagsfassung, der der Text fehlte; ich berichte beide Fakten und keine Minute der Veröffentlichung.4
- Die Landingpage. Um 11:49 a.m. zeigten die drei Kacheln unter „Tools and documentation“ alle „Coming soon“, ihre Links in HTML-Kommentaren hinterlegt. Um 9:51 p.m. verlinkt die Kachel „Documentation resources“ („Learn how to build and optimize your app for touch input.“) den Artikel und das Beispiel zu File Promises, und die Kachel „Design resources“ („Create experiences that take advantage of touch user interface patterns.“) verlinkt die drei HIG-Seiten und Apple Design Resources, deren Liste der Produktrahmen „MacBook Pro PROLED XDR Display“ enthält. Die Kachel „Xcode 27.3 beta“ zeigt weiterhin „Coming soon“, und der HTML-Kommentar
TK skillsteht noch in der Seite.5 - Das Beispiel. Die Seite zu „Supporting drag and drop through file promises“ sagt jetzt über ihre Recognizer: „The recognizers work the same way with a pointer or with direct touches on a Mac with a touch display.“ (Die Recognizer verhalten sich mit einem Zeiger und mit direkten Berührungen auf einem Mac mit Touch-Display gleich.)6
- Sonst hat sich nichts bewegt. Der neueste Eintrag im Developer-News-Feed ist weiterhin „Say hello to Touch on Mac“ um 10:00:55 a.m. PDT; die neuesten Einträge im Releases-Feed sind die Point-Releases vom 26. Oktober, ohne Beta von Xcode 27.3 oder macOS 27.3; die Tech-Talks-Liste enthält dieselben drei Talks; die ActivityKit-Seite trägt einen Stempel vom 8. Oktober und führt weiterhin keine macOS-Plattform.56
Der Nachmittagsbeitrag sagte, die schriftlichen Regeln seien die Talks und die Technote vom Juni. In diesem Beitrag geht es darum, was die schriftlichen Regeln hinzufügen, und er korrigiert den Nachmittagsbeitrag dort, wo die Prüfung am Abend andere Informationen ergab; die Liste steht am Ende.
Die Tabelle, die Ihren Code sortiert
Der Abschnitt „Prepare your app for a Mac with a touch display“ des Artikels ist eine Tabelle mit drei Spalten (Eingabeverarbeitung, Verhalten mit Touch, nächster Schritt) und vier Zeilen. Apples Verhaltensspalte ist zitiert; die beiden anderen Spalten sind verdichtet, außer beim nächsten Schritt der dritten Zeile, der Apples Wortlaut ist:1
| Ihr Code | Apples „Behavior with touch“ | Nächster Schritt |
|---|---|---|
| Standard-Bedienelemente und Gesture Recognizer von AppKit | „Works with touch, with no changes needed.“ | Testen. |
Overrides von mouseDown(with:) und Tracking-Schleifen |
„AppKit emulates a single-finger tap or drag, but supports multi-finger gestures and individual touches only through gesture recognizers.“ | Durch Recognizer ersetzen. |
Unterklassen von NSButton, NSSlider und anderen AppKit-Views, die Methoden zur Mausverarbeitung überschreiben |
„AppKit falls back to a tracking loop.“ | „Replace the overrides with target-action pairs for tracking events.“ |
Eigene NSGestureRecognizer-Unterklassen |
„A recognizer that never reaches a terminal state keeps its exclusive view, which can make your app appear unresponsive.“ | Abbruch behandeln und einen Endzustand erreichen. |
Der Test, der auf die Tabelle folgt, umfasst vier Zeilen und lohnt sich, genau so ausgeführt zu werden: „Controls respond to a tap, and buttons highlight and activate as expected“, „Dragging a view, selecting text, and scrolling work with one finger“, „Pinch and rotate work where your app supports them“ und „Small targets, such as resize grips and grab handles, are easy to hit with a finger“ (Bedienelemente reagieren auf Tippen, und Buttons werden wie erwartet hervorgehoben und aktiviert; das Ziehen einer View, Textauswahl und Scrollen gelingen mit einem Finger; Pinch und Drehen funktionieren, wo die App sie unterstützt; kleine Ziele wie Griffe zum Ändern der Größe und Anfasser sind mit dem Finger leicht zu treffen).1 Die Suchliste erweitert das Audit des Talks über Overrides von mouseDown(with:) auf fünf Symbole: „search your project for nextEvent(matching:until:inMode:dequeue:) and trackEvents(matching:timeout:mode:handler:), because tracking loops typically call these methods. Also search for mouseDown(with:) and mouseDragged(with:) overrides, and for uses of currentEvent in control action methods.“1 Die letzte Prüfung ist diejenige, um die es in der vierten Zeile geht: „confirm that your app stays responsive after a touch interaction ends, and that every gesture recognizer action method handles the NSGestureRecognizer.State.cancelled state.“1
Zwei Sätze im Overview klären eine Frage, die der Talk dem Schlussfolgern überließ. Die Emulation ist eine Ein-Finger-Emulation: „AppKit emulates mouse events from a single finger tapping or dragging“, und „Relying on this emulation, however, means you miss out on multi-finger gestures, like pinch and rotate, that gesture recognizers handle“ (wer sich auf die Emulation verlässt, verzichtet auf Mehrfingergesten wie Pinch und Drehen, die Gesture Recognizer verarbeiten).1 Und die Testumgebung ist die, die der Talk nannte, jetzt in einem Tipp: „Use Sidecar to test touch input without a Mac that has a touch display“, denn „AppKit then routes touch on its screen the same way it does on a Mac with a touch display.“1
Die Override-Regel
Hinter der dritten Zeile der Tabelle steht eine Regel, die weder der Talk noch die Technote vom Juni so deutlich ausgesprochen haben. Wenn Sie NSButton ableiten und mouseDown(with:) überschreiben, um eine Hervorhebungsanimation hinzuzufügen, „AppKit treats your override as a replacement for its own click handling, rather than as a side effect“ (behandelt AppKit Ihren Override als Ersatz der eigenen Klickverarbeitung, nicht als Nebeneffekt), und der Button kehrt zu einer Tracking-Schleife zurück.1 Dann: „AppKit decides based on whether your subclass overrides the method, not on what the override does“, sodass der Rückfall auch dann greift, wenn der Override super.mouseDown(with:) aufruft, und „Overriding mouseDragged(with:) or mouseUp(with:) has the same effect.“1
Der Ersatz ist das Muster mit Control-Events aus dem Talk, und das Listing des Artikels weicht in einem Detail, das sich zu übernehmen lohnt, von dem des Talks ab. Das Listing des Talks bei 2:24 registrierte eine Action für [.trackingExited, .trackingEndedInside] und eine separate für .trackingCancelled; die Menge zum Ausblenden im Artikel ist [.trackingExited, .trackingEndedInside, .trackingEndedOutside, .trackingCancelled] und ergänzt damit einen Druck, der außerhalb des Buttons endet.19 trackingEndedOutside ist nicht neu: Apples Dokumentation führt es ab macOS 11.0, und das macOS-27.0-SDK deklariert es in NSControl.h als NSControlEventTrackingEndedOutside = 1 << 7 (Analyse des Autors).67 Der Artikel weist außerdem darauf hin, dass die Action-Methoden mit @objc für Objective-C sichtbar sein müssen und dass primaryActionTriggered, wenn Sie nur die Aktivierung brauchen, „regardless of whether someone used a mouse, trackpad, or touch to produce it“ (unabhängig davon, ob Maus, Trackpad oder Touch sie ausgelöst hat) feuert.1
Container-Views haben dieselbe Falle: „If you start a drag from mouseDragged(with:) in an NSTableView, NSCollectionView, NSOutlineView, or NSBrowser, AppKit falls back to a tracking loop for the interaction“, und der Ausweg sind die Drag-Methoden von Data Source und Delegate, bei einer Tabelle tableView(_:pasteboardWriterForRow:).1
Für die beiden häufigen Overrides von mouseDown(with:) gibt der Artikel Vorher-nachher-Paare. „Most mouseDown(with:) overrides do one of two things: mark an item as selected when someone clicks it, or show a menu when someone Control-clicks“ (die meisten dieser Overrides tun eines von zwei Dingen: ein Element beim Klick als ausgewählt markieren oder bei Control-Klick ein Menü zeigen).1 Für die Auswahl beobachten Sie stattdessen isSelected am Collection-View-Item oder an der Table-Row-View, mit dem Vorbehalt, dass der Beobachter „also runs when your code changes the selection programmatically“; für das Menü überschreiben Sie menu(for:) und lassen AppKit es anzeigen, oder Sie setzen menu oder defaultMenu, wenn das Menü nicht vom Klick abhängt.1
Ein reiner Touch-Recognizer neben einer Schleife, die Sie behalten
Der Talk empfahl einen reinen Touch-Pan-Recognizer neben einer bestehenden Tracking-Schleife als Weg für ein Punkt-Update. Der Artikel dokumentiert dasselbe Rezept unter „Add touch support to your custom tracking loop“, mit den gemeinsam genutzten Funktionen für Start, Fortsetzung, Ende und Abbruch, und gibt dann an, was jede Property bewirkt, mit einer Genauigkeit, für die im Talk kein Platz war:1
let touchRecognizer = NSPanGestureRecognizer(target: self, action: #selector(handleTouchPan))
touchRecognizer.buttonMask = 0
touchRecognizer.allowedTouchTypes = .direct
addGestureRecognizer(touchRecognizer)
„setting buttonMask to 0 means the gesture recognizer only recognizes touches, not mouse or trackpad input, so your tracking loop keeps handling the mouse and trackpad exactly as before.“1 Lassen Sie die Maske auf ihrem Standardwert, dann „also responds to the primary mouse button. A mouse drag then begins the gesture recognizer, and AppKit doesn’t call your mouseDown(with:) override, so the tracking loop doesn’t run. A click that doesn’t move still reaches your tracking loop“ (reagiert der Recognizer auch auf die primäre Maustaste; ein Ziehen mit der Maus startet dann den Recognizer, AppKit ruft Ihren Override von mouseDown(with:) nicht auf, und die Tracking-Schleife läuft nicht; ein Klick ohne Bewegung erreicht Ihre Tracking-Schleife weiterhin).1 Die Maske ist also keine Nebensache; ohne sie bekommt die Schleife stillschweigend keine Ziehbewegungen mehr. Die Zeile mit allowedTouchTypes ist doppelte Absicherung: „Direct touches are already the default for allowedTouchTypes, and setting it to .direct states that intent explicitly.“1 Apples eigene Einschätzung des Ansatzes: „This is often the fastest, lowest-risk way to add touch support to code with a tracking loop you’d rather not rewrite all at once.“1
Ersetzen Sie die Schleife stattdessen, setzt das Pan-Beispiel des Artikels die Beschriftung bei .cancelled auf ihren Ausgangspunkt zurück, und eine Schleife, die eine Drag-Session gestartet hat, übergibt beginDraggingSession(items:gesture:source:) den Recognizer statt des Events.1
Filtern mit den beiden Delegate-Methoden
Der Abschnitt, der gegenüber den Talks am meisten Neues bringt, ist „Filter gesture recognizer candidates“. Recognizer werden per Hit-Testing gesammelt, nicht über die Responder Chain: „Gesture recognizers don’t use the responder chain.“1 Wenn AppKit sie sammelt, „not every recognizer it finds is actually applicable to the interaction“ (passt nicht jeder gefundene Recognizer tatsächlich zur Interaktion), und die App entfernt die unpassenden über zwei Delegate-Methoden: gestureRecognizer(_:shouldAttemptToRecognizeWith:) „to inspect the initiating NSEvent for pointer-based input“ und gestureRecognizer(_:shouldReceive:) „to inspect each new NSTouch for touch input“.1 Der Sinn beider: „Either method gives you the initial input, something the recognizer doesn’t otherwise expose, and lets you stop the recognizer from handling that event stream or touch.“1 Und die Trennung ist strikt: „Direct touches don’t call this method. To filter them, implement gestureRecognizer(_:shouldReceive:) instead, as shown above.“1 Wer nur über die Event-Methode filtert, lässt direkte Berührungen von diesem Delegate ungefiltert.
Die beiden Beispiele sind Muster, die meiner Erwartung nach kopiert werden. Ein Griff zum Ändern der Größe: Hängen Sie den Resize-Recognizer an die ganze View und geben Sie in beiden Delegate-Methoden zurück, ob die Position im Rahmen des Griffs liegt, wobei true „only to give the gesture recognizer a chance to attempt recognition, not to guarantee it succeeds“ (nur eine Chance zur Erkennung gibt, keinen Erfolg garantiert) zurückgegeben wird.1 Eine Zeitleiste aus Clips: ein Recognizer auf der Zeitleiste, und in der Event-Methode wird der Clip per Hit-Test ermittelt, für die Action-Methode gespeichert und false zurückgegeben, wenn es keinen gibt.1 Die begleitende Warnung für kleine Ziele: „If a custom control expects a touch to land inside its visible area, update it to handle touches that land outside that area.“1
Die Priorität zwischen den verbleibenden Recognizern ist eine Fehlschlag-Anforderung, und der Artikel wiederholt die Warnung aus dem zweiten Artikel, „Use this sparingly, because the waiting recognizer responds more slowly“, und nennt als Beispiel, dass „making a single click wait for a double click to fail makes clicks feel sluggish“ (Klicks wirken träge, wenn ein Einfachklick auf das Scheitern eines Doppelklicks wartet).1
currentEvent, Hover und Wischgesten
„Handle touch input“ wiederholt den ordnenden Satz aus dem Talk in Dokumentationsform: AppKit „doesn’t deliver direct touches (NSTouch) through the responder chain or through event monitors“, und „Gesture recognizers are the only way to respond to multi-finger gestures or to receive individual touches from a Mac with a touch display or a Sidecar-connected iPad.“1 Die Übersichtsseite zur Eingabe in AppKit sagt dasselbe von der Responder-Seite aus: „The responder chain receives touch events only from indirect touches on a trackpad, if a view asks for them. It doesn’t receive touch events from direct touches on a Mac with a touch display“ (die Responder Chain erhält Touch-Events nur von indirekten Berührungen auf einem Trackpad, wenn eine View sie anfordert; von direkten Berührungen auf einem Mac mit Touch-Display erhält sie keine).6
Bei Prüfungen der Gerätefähigkeit lautet der Rat, sie zu vermeiden: „In most cases, instead of checking whether a device supports touch, write your event-handling code to work the same way regardless of input source, and decide on alternate presentations based on the interaction type.“1 Das einzige Beispiel, das für touchCapabilities genannt wird, ist „capturing a signature on a touch display“ (eine Unterschrift auf einem Touch-Display erfassen).1
currentEvent bekommt einen eigenen Abschnitt. Weil Interaktionen mit Bedienelementen über Recognizer laufen, „currentEvent no longer reliably returns the event that initiated an action“, und das Vorher-nachher-Paar ersetzt die Modifier-Flags des Events durch das klassenweite NSEvent.modifierFlags, mit dem Vorbehalt: „These report the state at the time you query them, not at the time of the original event. A touch doesn’t register in pressedMouseButtons“ (sie melden den Zustand zum Zeitpunkt der Abfrage, nicht des ursprünglichen Events; eine Berührung erscheint nicht in pressedMouseButtons).1 Was sich nicht ändert: „Keyboard events and scroll-wheel events behave the same on every input source: AppKit delivers them through the responder chain. Hover tracking, which relies on mouseMoved(with:) and NSTrackingArea, applies to pointer input only, because a touch display has no hover.“1
Wischgesten werden nicht übersetzt: „AppKit doesn’t translate touch swipes for you.“1 Wenn Sie trackSwipeEvent(options:dampenAmountThresholdMin:max:usingHandler:) verwenden, implementieren Sie Ihre eigenen, und prüfen Sie für die Vor- und Zurück-Navigation zuerst NSEvent.isTouchSwipeNavigationEnabled, „because the person can turn off swipe navigation in System Settings“ (weil die Person die Wischnavigation in den Systemeinstellungen abschalten kann).1 Diese Property ist neu: Apples Dokumentation führt sie ab macOS 27.0, und NSEvent.h im macOS-27.0-SDK deklariert sie in Zeile 496 als API_AVAILABLE(macos(27)) (Analyse des Autors).67
Eigene Recognizer: der Vertrag, das Timeout und zwei Defaults
Die Richtlinien des Artikels für eine eigene NSGestureRecognizer-Unterklasse bilden einen Vertrag in sieben Punkten: Abbrüche in mouseCancelled(with:) und touchesCancelled(with:) behandeln; in den Event-Methoden nur den privaten Tracking-Zustand aktualisieren und state setzen; in der Action-Methode reagieren, nicht im Event-Callback; das auslösende NSEvent oder NSTouch für die Action-Methode zwischenspeichern; „Override reset() to clear your cached state, so it doesn’t leak from one gesture into the next“; „Always end an active gesture recognizer by transitioning to NSGestureRecognizer.State.ended, NSGestureRecognizer.State.failed, or NSGestureRecognizer.State.cancelled. Otherwise, other recognizers can’t activate“; und location(in:) überschreiben, denn „If you skip this override and build your app with the macOS 27 SDK and later, AppKit returns NSZeroPoint and logs a console error“ (ohne diesen Override liefert AppKit beim Build mit dem macOS-27-SDK oder neuer NSZeroPoint und protokolliert einen Fehler in der Konsole).1 Das folgende Beispiel DragTrackingRecognizer ist ein Gerüst: Die Rümpfe seiner Overrides für Mouse-down, Dragged und Up sind Kommentare, die anweisen, eine private Methode track(_:event:state:) aufzurufen, der Rumpf des Abbruch-Overrides weist an, .cancelled zu setzen, und die Action-Methode liest die zwischengespeicherte Position und das Event zurück.1
Im zweiten Artikel werden die Folgen eines Vertragsbruchs beziffert. „A gesture recognizer is stuck if it hasn’t reached a terminal state within 2 seconds after the person stops interacting with your app, such as when they release the mouse button or lift their fingers“ (ein Recognizer gilt als hängend, wenn er nicht innerhalb von 2 Sekunden, nachdem die Person die Interaktion mit Ihrer App beendet hat, etwa durch Loslassen der Maustaste oder Abheben der Finger, einen Endzustand erreicht hat).2 Unter exklusiven Views gilt: „A stuck gesture recognizer keeps holding its exclusive view“, was das Sammeln außerhalb seiner Hierarchie blockiert, und die Abhilfe des Systems erfolgt automatisch: „To keep a single stuck gesture recognizer from blocking your app indefinitely, the system automatically transitions it to the NSGestureRecognizer.State.cancelled state, allowing gesture gathering to resume normally.“2 Das ist der Mechanismus hinter der vierten Zeile der Tabelle, und er bedeutet, dass ein fehlerhafter Recognizer in einer ausgelieferten App das Sammeln von Gesten außerhalb seiner exklusiven Hierarchie blockiert, sodass der Rest der App nicht zu reagieren scheint, bis zum automatischen Abbruch zwei Sekunden nachdem die Person die Interaktion beendet hat, wobei das Ziel der Action weiterhin für die Behandlung des Abbruchs zuständig ist.
Zwei User Defaults lassen diese Probleme während der Entwicklung mit einem Stack-Trace abstürzen, einer bei einem fehlenden Override und einer anstelle des stillen Abbruchs. „Set the NSGestureRecognizerCrashOnMissingOverrides key in UserDefaults to YES“, oder übergeben Sie -NSGestureRecognizerCrashOnMissingOverrides YES als Startargument, damit die App abstürzt, wenn eine Event-Methode fehlt, die Sie hätten überschreiben sollen; und setzen Sie NSCrashOnStuckGestureTimeout „to a small number of seconds, such as 2“ (auf wenige Sekunden, etwa 2), oder übergeben Sie -NSCrashOnStuckGestureTimeout 2, damit ein hängender Recognizer einen Absturz auslöst, statt abgebrochen zu werden.2 Der erste Default fängt nicht alles ab: Er „doesn’t detect a gesture recognizer that correctly transitions to NSGestureRecognizer.State.cancelled and sends its action, if the action’s target doesn’t handle the canceled state“ (erkennt keinen Recognizer, der korrekt in NSGestureRecognizer.State.cancelled übergeht und seine Action sendet, wenn das Ziel der Action den abgebrochenen Zustand nicht behandelt).2 Die anschließende Warnung gehört auf eine Checkliste: „Comment out or remove NSGestureRecognizerCrashOnMissingOverrides and NSCrashOnStuckGestureTimeout before shipping your app“, und „Changes to these user defaults or launch arguments take effect only after you rebuild or relaunch your app.“2 Keiner der beiden Defaults kommt in den Talks oder in der Technote vom Juni vor.
Derselbe Artikel erklärt den Unterschied zwischen den beiden Endzuständen, die kein Erfolg sind: .failed ist für einen Recognizer gedacht, der nie gepasst hat, und „If a gesture recognizer sets this after it already delivered an action, the system automatically converts it to NSGestureRecognizer.State.cancelled instead.“2 Außerdem nennt er über das Exposé-Beispiel aus dem Talk hinaus eine weitere Ursache für einen Abbruch durch das System: „when the same touches begin a system-level gesture like smartMagnify(with:), the system takes over those touches for its own use“ (wenn dieselben Berührungen eine Systemgeste wie smartMagnify(with:) beginnen, übernimmt das System diese Berührungen für sich).2
Drei kleinere Fakten von derselben Seite. Recognizer können Responder-Events verzögern: „If you set delaysPrimaryMouseButtonEvents to true, the system stores the relevant events until the gesture recognizer fails, then delivers them to the appropriate responders. If the gesture recognizer reaches the NSGestureRecognizer.State.ended state, the system never delivers the events.“2 Und die eigenen Touch-Methoden des Responders gelten nur für das Trackpad: „These methods don’t have an equivalent delay mechanism, and they apply only to indirect touches from a trackpad.“2 Ein View Controller kann keinen Recognizer halten: „You can’t attach a gesture recognizer to a view controller directly“, also hängen Sie ihn an die View des Controllers, mit dem Controller als Ziel und Delegate.2
Exklusive Views: standardmäßig an, in Xcodes AppKit-Templates aus
Der Nachmittagsbeitrag erklärte die Exklusivität anhand des Talks und der Technote vom Juni. Die beiden Artikel ergänzen den Standardwert, den Mechanismus und eine Tatsache zu Xcode.
Der Mechanismus, neu in macOS 27: „In macOS 27 and later, the initial hit-tested view for a mouse click or touch event becomes an exclusive view for gathering gesture recognizers“, und „This behavior simulates the presence of a single mouse pointer, even when a person touches a Mac with a touch display with several fingers at once“ (das Verhalten simuliert einen einzigen Mauszeiger, auch wenn jemand einen Mac mit Touch-Display mit mehreren Fingern zugleich berührt).2 Der Standardwert: „Exclusive views are on by default“, und „If your app’s information property list doesn’t include the NSViewGestureRecognizerIsExclusive key, the system treats it as YES.“2 Die Seite des Schlüssels selbst stimmt zu, „The default value is YES“, und fasst das Opt-out eng: „Set this key to NO to allow gesture recognizers in multiple views to activate simultaneously. Do this only if your app intentionally needs concurrent gesture recognition across different views“ (nur dann, wenn die App absichtlich gleichzeitige Gestenerkennung in verschiedenen Views braucht).6 Das Beispiel des Systems für ein Opt-out pro View ist das Farbfenster: „AppKit uses this same technique in the standard Color Panel“, wo sich mehrere Slider gleichzeitig bewegen.2
Die Tatsache zu Xcode, in beiden Artikeln genannt: „Xcode adds the NSViewGestureRecognizerIsExclusive key with a value of NO to new macOS app projects, so those apps don’t use exclusive views unless you change the value to YES.“1 Ich habe die Projekt-Templates auf diesem Mac gelesen, um zu sehen, welche Projekte das betrifft (Analyse des Autors). In Xcode 27.1 (27A9275) und in der Xcode-27.2-Beta (27B5019j) erbt das Template macOS App von einer Einheit „Cocoa App Base“, deren Option für den App-Lebenszyklus zwei Werte hat, SwiftUI und Cocoa, mit SwiftUI als Standard; der Schlüssel INFOPLIST_KEY_NSViewGestureRecognizerIsExclusive mit dem Wert NO steht nur in der Cocoa-Einheit, Xcode schreibt ihn also als Build-Einstellung, die in der generierten Info.plist landet, wenn das Projekt den Lebenszyklus mit AppKit-App-Delegate verwendet, und nicht beim SwiftUI-Lebenszyklus.7 Die Lebenszyklus-Option trägt in den Templates keinen Anzeigenamen; die Option, die einen hat, ist Interface: (SwiftUI, Storyboard oder XIB, Standard SwiftUI), und die Einheiten Storyboard und XIB setzen den AppKit-Lebenszyklus voraus, sodass ein Storyboard- oder XIB-Projekt den Schlüssel bekommt und ein Projekt mit SwiftUI-Interface nur dann, wenn sein Lebenszyklus der AppKit-App-Delegate ist.7 Das Template Document App stammt von derselben Basis ab, erbt also unter derselben Bedingung diese Build-Einstellung und schreibt den Schlüssel zusätzlich als Info.plist-Eintrag mit dem Wert false; das macOS-Template Game erzwingt den Cocoa-Lebenszyklus, sodass ein neues macOS-Spiel nicht exklusiv startet.7 Auf diesem Mac ist kein Xcode älter als 27.1 installiert, daher kann ich nicht sagen, wann die Templates den Schlüssel bekamen. Praktisch gelesen: Ein AppKit-Projekt, das aus diesen Templates entsteht, startet nicht exklusiv; ein SwiftUI-Projekt und jede bestehende App, deren Info.plist keinen Schlüssel hat, ist exklusiv. Der Migrationshinweis des zweiten Artikels folgt daraus: Hat Ihre App den Schlüssel auf NO gesetzt, „also set the view’s exclusiveGestureBehavior to NSView.ExclusiveGestureBehavior.exclusive“, wenn Sie eine Tracking-Schleife ersetzen, denn „This prevents simultaneous gesture recognition with unrelated views, which the loop previously prevented implicitly“ (das verhindert gleichzeitige Erkennung mit fremden Views, was zuvor die Schleife implizit verhinderte).2
Der erste Artikel entwirrt die drei Einstellungen, die Exklusivität und Scrollen berühren, mit dem Satz „These settings don’t override each other“ (diese Einstellungen setzen einander nicht außer Kraft).1 exclusiveGestureBehavior und der plist-Schlüssel entscheiden, welche Recognizer AppKit sammelt, und „The view’s setting applies to that view and its subviews, and takes precedence over the app-wide key.“1 isCancellableByScrollGesture gilt pro Recognizer und unabhängig davon: „It lets a scroll gesture cancel that gesture recognizer even if it has already begun.“1 scrollGestureForRelationships ist für Recognizer gedacht, die mit denen der Scroll-View konkurrieren.1 Das Szenario, auf das man laut Artikel achten soll, ist ein Button in einer Scroll-View: Setzen Sie isCancellableByScrollGesture an seinem Recognizer auf true, damit ein Ziehen, das auf dem Button beginnt, scrollt statt zu klicken.1
Text-Engines bekommen ein weiteres Werkzeug. NSTextSelectionManager „creates its gesture recognizers itself, so you don’t have a reference to them“ (erzeugt seine Recognizer selbst, Sie haben also keine Referenz darauf), daher gibt ein eigener Recognizer, der auf sie warten muss, aus gestureRecognizer(_:shouldRequireFailureOf:) true zurück, wenn gesturesForFailureRequirements des Managers den anderen Recognizer enthält.1 Diese Property ist ab macOS 27.0 aufgeführt und in NSTextSelectionManager.h in Zeile 59 des macOS-27.0-SDK deklariert (Analyse des Autors).67
Emulation, in fünf Punkten
Der Nachmittagsbeitrag zitierte die vier Emulationsregeln der Technote vom Juni für UI-Frameworks, die nicht von AppKit stammen. Die Liste des Artikels hat fünf, und die neue betrifft das Ziehen.18
- „Tapping emulates a mouse-down followed immediately by a mouse-up.“ (Tippen emuliert ein Mouse-down, unmittelbar gefolgt von einem Mouse-up)
- „Touching and then immediately panning emulates a trackpad scroll, even if the UI doesn’t otherwise handle Multi-Touch input directly.“ (Berühren mit sofortigem Schwenken emuliert einen Trackpad-Scroll, auch wenn die UI Multi-Touch-Eingaben sonst nicht direkt verarbeitet)
- „Touching, briefly holding, and then moving emulates mouseDown(with:), followed by mouseDragged(with:) as it moves, and mouseUp(with:) on lift.“ (Berühren, kurz Halten und dann Bewegen emuliert mouseDown(with:), gefolgt von mouseDragged(with:) während der Bewegung und mouseUp(with:) beim Abheben)
- „Touching and holding without movement emulates rightMouseDown(with:), and rightMouseUp(with:) on lift.“ (Berühren und Halten ohne Bewegung emuliert rightMouseDown(with:) und beim Abheben rightMouseUp(with:))
- „Pinching or rotating with two fingers emulates the trackpad magnify and rotate gestures.“ (Pinch oder Drehen mit zwei Fingern emuliert die Trackpad-Gesten für Vergrößern und Drehen)
Der dritte Punkt ist die schriftliche Form von „drag with a small hold delay“ (Ziehen mit kurzer Halteverzögerung) aus dem Talk (11:31): Eine Berührung, die sich sofort bewegt, scrollt, eine Berührung, die zuerst hält, zieht.9 Die Liste vom Juni hatte ein Tippen, ein sofortiges Schwenken, ein langes Drücken und ein Zusammenziehen oder Drehen mit zwei Fingern, aber keinen Punkt für das Ziehen.8 Die Grenze sind zwei Finger: „This emulation covers interactions with one or two fingers. If another finger touches down during a pinch or rotation, the emulation ignores it and keeps tracking the original two fingers.“1 Drei oder mehr Finger brauchen Recognizer, und NSIsTouchNative mit dem Wert YES schaltet die zusätzliche Emulation ab, sobald das Framework Touch selbst verarbeitet.1
Spiele: TN3216
Die Technote richtet sich an Spiele: „This guidance focuses on configurations common in games, such as an app with a single view backed by a CAMetalLayer.“3 Ihre Design-Rahmung ist strenger als die der HIG: „Touch extends the Mac experience rather than replacing other input“ (Touch erweitert das Mac-Erlebnis, statt andere Eingaben zu ersetzen), und „Touch works best for brief interactions that complement these devices“, mit der Anweisung „Design touch interactions for brief moments that the player can complete with one hand, while the other hand stays on the keyboard, mouse, or controller“ (kurze Momente, die sich mit einer Hand erledigen lassen, während die andere an Tastatur, Maus oder Controller bleibt), für Interaktionen „such as inventory management, map zoom and scroll, or the settings menu“.3
Die Kompatibilitätsgeschichte ist dieselbe Emulation mit einer Wendung für Spiele. „AppKit applies game-specific emulation to apps in a games category, and can apply emulation to an individual view, such as one that hosts a third-party UI framework.“3 Die Kategorie wird aus der Info.plist gelesen: Ist LSApplicationCategoryType gleich public.app-category.games oder eine Unterkategorie für Spiele, „the system applies game-specific mouse emulation“.3 Das Problem, das sie löst, hat eine Maus nie: „A mouse moves continuously from one position to the next, but a player can tap or start a drag at two distant locations in succession“ (eine Maus bewegt sich stetig, ein Spieler kann aber nacheinander an zwei weit entfernten Stellen tippen oder ziehen), daher driftet ein Spiel, das Deltas aufsummiert, und „For games, AppKit sends a mouseMoved event to the new location before each mouseDown(with:), so your game can refresh its cached mouse position.“3
Anschließend führt die Technote die emulierten Events in ihrer Reihenfolge auf. Bei einem Tippen: mouseMoved(with:) „from the last mouse position to the tap location“, dann mouseDown(with:), dann mouseUp(with:). Bei einem Ziehen: mouseMoved(with:) zu der Stelle, an der das Ziehen beginnt, mouseDown(with:), mouseDragged(with:) „repeatedly while the touch continues“ und mouseUp(with:).3 Dann eine Regel in einem Kasten „Important“: „Don’t intercept events by overriding sendEvent(_:) or pulling them with nextEvent(matching:until:inMode:dequeue:). Doing so can interfere with gesture recognizers that respond to a physical mouse.“3
Der Teil ohne Gegenstück in den Talks ist GCMouse. „By default, the system presents each touch surface, whether a Mac with a touch display or a Sidecar-connected iPad, as an additional GCMouse that emulates mouse input from touches“ (standardmäßig stellt das System jede Touch-Oberfläche als zusätzliche GCMouse dar, die aus Berührungen Mauseingaben emuliert): Ein aufgesetzter Finger drückt die linke Taste, eine Bewegung ruft den Handler für Mausbewegungen mit einem Delta auf, Abheben lässt los.3 Ihre Grenzen: „The emulated GCMouse ignores additional fingers, and it doesn’t emulate other buttons, scrolling, or pinch input“, und weil Berührungen an unzusammenhängenden Stellen landen, gilt: „Don’t use them to track an absolute pointer position.“3
NSIsTouchNative bewirkt bei einem Spiel mehr als bei einer AppKit-App. Auf YES gesetzt, „AppKit’s emulation will stick to a generic emulation - ignoring the app’s category or whether it uses a third-party UI framework - and disable GCMouse emulation“ (bleibt AppKit bei der generischen Emulation, ohne Rücksicht auf die Kategorie der App oder darauf, ob sie ein UI-Framework eines Drittanbieters nutzt, und schaltet die GCMouse-Emulation ab), sodass die Touch-Oberfläche nicht mehr als GCMouse erscheint und „Your game receives touch through gesture recognizers instead of emulated mouse events.“3 Ein Rest bleibt: „AppKit still emulates mouse events for any NSView that handles them, for example by overriding mouseDown:.“3 Die Seite des Schlüssels sagt, dass die meisten AppKit-Apps ihn nicht brauchen und dass er „primarily intended for apps that embed a non-AppKit rendering or input framework and implement their own touch-handling pipeline“ ist; eine Spiel-Engine ist genau dieser Fall.6
Der Implementierungsabschnitt ist ein Delegate-Muster in Swift und Objective-C: ein Pan- und ein Tap-Recognizer mit buttonMask 0, damit sie eine physische Maus ignorieren, ein Magnification-Recognizer, shouldRecognizeSimultaneouslyWith, das nur für das Paar aus Pan und Pinch true zurückgibt, und shouldRequireFailureOf, das den Tap auf beide warten lässt.3 Ein Hinweis bestätigt den Standardwert, den der erste Artikel nennt: „The gesture recognizers must include direct in allowedTouchTypes. On macOS 27 and later, this is the default.“3 Der Rat zum Routing ist auf Engines zugeschnitten: „Install the gesture recognizers once in your app and leave them enabled“, Callbacks in Eingabe-Events der Engine umwandeln und „Let the active input handler decide if and how to react to different gestures“ (der aktive Input-Handler entscheidet, ob und wie er auf Gesten reagiert), mit einem Pan-Handler, der Punkte mit dem Backing Scale Factor in Pixel umrechnet und die y-Achse für eine Engine spiegelt, deren Ursprung oben links liegt.3 Standard-Bedienelemente funktionieren nach dem Umstieg weiter, „as long as you don’t attach your own direct-touch gesture recognizers to them“.3
Die abschließende Checkliste hat zwei Hälften. Für ein Spiel mit Emulation: die Position aus jedem Event lesen und weder sendEvent: überschreiben noch aus nextEventMatchingMask: ziehen. Für ein Spiel, das Touch selbst verarbeitet: NSIsTouchNative setzen, Recognizer für direkte Berührungen mit buttonMask 0 verwenden, Touch dort ergänzen, wo Spieler es erwarten, und einhändig halten, „Continue to read GCMouse for the physical mouse. With NSIsTouchNative set, touch no longer appears as an emulated GCMouse“ (GCMouse weiter für die physische Maus lesen; Touch erscheint dann nicht mehr als emulierte GCMouse), und wo möglich Standard-Bedienelemente nutzen.3
Die HIG, datiert auf den 28. Oktober
Der Nachmittagsbeitrag sagte, die vier HIG-Seiten, die die Landingpage verlinken wollte, enthielten keine Hinweise zu Touch auf dem Mac-Display. Drei davon enthalten jetzt welche, jeweils mit einer Zeile im Änderungsprotokoll vom 28. Oktober 2026; die Live-Activities-Seite ist unverändert.4
Designing for macOS („Added guidance for touch input.“) schreibt drei Zeilen ihrer Plattformbeschreibung neu und ergänzt eine Best Practice. Ergonomie: „On a Mac with a touch display, people reach forward to touch the screen, so they tend to use touch for brief, intentional interactions rather than sustained work“ (wer nach vorn zum Bildschirm greift, nutzt Touch eher für kurze, gezielte Interaktionen als für längere Arbeit). Bei den Eingaben stehen jetzt Tastaturen und Zeigegeräte vorn, und unter den übrigen ist „Gestures on a trackpad or a Mac with a touch display“ aufgeführt. Die neue Best Practice fasst den ganzen Design-Talk in einem Satz zusammen: „Support standard gestures on trackpads and Mac devices with a touch display. Use larger tap targets and controls that expand on touch to make touch interaction easier, while keeping control size and density the same to stay optimized for keyboard and pointing device input“ (Standardgesten auf Trackpads und Macs mit Touch-Display unterstützen; größere Tap-Ziele und sich bei Touch vergrößernde Bedienelemente erleichtern die Touch-Interaktion, während Größe und Dichte der Bedienelemente gleich bleiben, damit alles für Tastatur und Zeigegerät optimiert bleibt).4
Gestures („Added guidance for gestures and touch input in macOS.“) erweitert ihren macOS-Abschnitt, der jetzt beide AppKit-Artikel als Hinweise für Entwickler verlinkt. Sein erster Satz legt den Rang fest: „On a Mac with a touch display, or on an iPad connected with Sidecar, people can also interact directly using touch gestures as a secondary input mode.“4 Dann sechs Regeln, jede mit fett gesetztem Anfang:
- „Keep the layout and size of interface elements the same when you support touch“ (Layout und Größe der Oberflächenelemente gleich lassen, wenn Sie Touch unterstützen). Die Begründung: „In macOS, touch is secondary to keyboard and pointing device input“, und Menschen wechseln oft zwischen den Eingaben. Der Kontrast zum iPhone Duo, dessen inneres Display die Size Classes wechselt, ist der Punkt: Beim Mac ändert sich die Eingabe, nicht das Layout.
- „Make tap targets bigger, not the controls themselves“, indem man „enlarging their tap targets to anticipate that interaction may start slightly outside of their visual bounds“ (die Tap-Ziele vergrößert, weil eine Interaktion knapp außerhalb der sichtbaren Grenzen beginnen kann), und „Where possible, use standard framework controls, which the system configures with an optimal tap target size automatically.“
- „Consider providing expandable controls“: eine vorübergehende, touchgroße Variante, die beim Tippen erscheint, beim Halten bleibt und „for a short time after they lift their finger to keep the control accessible to touch“ (kurz nach dem Abheben des Fingers, damit das Bedienelement per Touch erreichbar bleibt) stehen bleibt; „When using standard system controls, steppers, sliders, and dials gain this behavior automatically.“
- „Don’t use touch as the only way to access controls or features“ (Touch nicht als einzigen Zugang zu Bedienelementen oder Funktionen nutzen), und umgekehrt: „aim to provide touch equivalents for actions otherwise performed exclusively with a pointing device, like using a keyboard modifier to shift-drag or displaying a menu when the pointer moves over an element“ (Touch-Entsprechungen für Aktionen anbieten, die sonst nur mit einem Zeigegerät möglich sind, etwa Shift-Ziehen mit einer Sondertaste oder ein Menü, das erscheint, wenn der Zeiger über ein Element fährt).
- „Where possible, align with gestures people already know“, aus Ihrer iPhone- oder iPad-App oder vom Trackpad, und „Test to ensure they don’t conflict with macOS system gestures.“
- „Make sure custom gestures work for both touch and trackpad“, mit „modifier keys where needed to help the gesture work for all touch inputs“ (Sondertasten, wo nötig, damit die Geste mit allen Touch-Eingaben funktioniert).4
Die Seite nennt die Zahl von 36 Punkten nicht; sie steht in den beiden Talks („Touch on macOS uses a touch target of 36 points“, 9:17 im Design-Talk, und die Vergrößerung der Trefferfläche im AppKit-Talk auf „a minimum of 36 points on each axis“, 5:53).9 Sie ergänzt eine Tabelle, „macOS trackpad and touch display gestures“, die jeder Aktion ihre Form auf dem Trackpad und auf dem Touch-Display zuordnet: Auswählen ist ein primärer Klick oder ein Tippen; Öffnen ein Doppelklick oder doppeltes Tippen; Scrollen geschieht mit zwei Fingern auf dem Trackpad und mit einem auf dem Display; Zoomen und Drehen sind auf beiden dasselbe Zusammenziehen und dieselbe Drehung mit zwei Fingern; ein Kontextmenü ist ein sekundärer Klick oder Berühren und Halten; Ziehen ist Klicken und Ziehen oder „Touch and hold, then drag“.4 In der plattformübergreifenden Tabelle führt „Touch (or pinch) and hold“ jetzt macOS, und die Zeile zum doppelten Tippen ergänzt „open an item on a Mac with a touch display“.4
Pointing devices („Added guidance for revealing hidden controls with touch on Mac, and updated guidance and artwork for iPadOS and macOS.“) ist die Seite mit den konkretesten Regeln, unter einer neuen Überschrift, „Touch support for hidden controls“. Die Prämisse: „Some Mac apps reveal certain controls only when the pointer moves over them. These controls need another way to appear when someone uses touch on a Mac that supports it“ (manche Mac-Apps zeigen bestimmte Bedienelemente nur, wenn der Zeiger darüberfährt; diese brauchen einen anderen Weg zu erscheinen, wenn jemand Touch auf einem Mac nutzt, der es unterstützt).4 Die Regeln:
- „Decide which hidden controls need touch support. Reserve touch support for essential controls“ (entscheiden, welche verborgenen Bedienelemente Touch-Unterstützung brauchen, und sie den essenziellen vorbehalten). Tooltips zählen nicht dazu, und ebenso wenig alles, was Touch schon auf anderem Weg erledigt: „someone can swipe to scroll a carousel, so there’s no need to reveal the navigation buttons that exist for mice without a scroll wheel.“ Und: „Avoid keeping hidden controls visible all the time just to accommodate touch.“
- „Reveal a hidden control when someone taps its general location on a touch display“ (ein verborgenes Bedienelement einblenden, wenn jemand auf dem Touch-Display in seine ungefähre Gegend tippt). Nur die relevanten, und „Hide a control again when the person finishes with it, or after a few seconds if they don’t use it“ (wieder ausblenden, wenn die Person fertig ist, oder nach ein paar Sekunden ohne Nutzung).
- „Retain the current selection when revealing a hidden control“, weil das Bedienelement auf die Auswahl wirken kann.
- „Make essential hidden controls available in a context menu“, das Touch mit Berühren und Halten öffnet.
- „In a sidebar, reveal hidden controls when someone taps the corresponding section header or row.“ Das Beispiel ist der Finder: „tapping the Favorites header in the Finder sidebar collapses the section and reveals a disclosure button.“ Die eingeblendeten Bedienelemente bleiben, bis Touch die Seitenleiste verlässt oder ein Zeigegerät benutzt wird, und ein Tippen auf die Fläche eines ausgeblendeten Bedienelements blendet es ein, ohne es auszulösen: „The control acts only when they tap it again“ (erst ein zweites Tippen löst es aus).
- „In a tabbed window, reveal the selected tab’s close button when someone taps anywhere in the window“ (in einem Fenster mit Tabs den Schließen-Button des aktiven Tabs bei einem Tippen irgendwo im Fenster einblenden), und ihn behalten, bis die Person zu einem Zeigegerät zurückkehrt.4
Diese sechs Regeln sind die schriftliche Form der Hover-Hinweise aus den beiden Talks, mit Zeiten und Ausnahmen, die die Talks nicht nannten: einige Sekunden, bevor ein eingeblendetes Bedienelement wieder verschwindet, ein zweites Tippen zum Auslösen, und Seitenleisten sowie Schließen-Buttons von Tabs als die Fälle, die sichtbar bleiben. Dieselbe Revision schrieb das Zeigermaterial der Seite für iPadOS auf Liquid Glass um, änderte im Text die Standardform des Zeigers von einem Kreis zu einem Pfeil, ersetzte die reine AppKit-Spalte der Cursor-Tabelle durch Symbole für AppKit und SwiftUI und entfernte den Abschnitt zum Zeiger-Magnetismus; nichts davon betrifft das Mac-Display.4
Was noch fehlt
Geprüft um 9:51 p.m. PT am 28. Oktober, gegen Apples Dokumentations-Endpunkt, die Landingpage, die Developer-News- und Releases-Feeds und die Tech-Talks-Liste.56
- Die Xcode-27.3-Beta. Die Kachel der Landingpage zeigt weiterhin „Coming soon“, der Releases-Feed führt nichts Neueres als die Point-Releases vom 26. Oktober (darunter macOS 27.2), und die Seiten mit den Release Notes zu Xcode 27.3 und macOS 27.3 geben 404 zurück. Das neueste Xcode bleibt der 27.2 RC, dessen SDK ich nicht untersucht habe; der Vorbehalt des Nachmittagsbeitrags zum Deployment-Target gilt weiterhin, und der Beitrag zum Release von Xcode 27 liefert den Hintergrund zu Deployment-Targets.
- Die drei Schalter für Systemgesten, die der AppKit-Talk vorführte:
NSWindow.disablesDirectTouchSystemGestures,UIViewController.prefersDirectTouchSystemGesturesDisabledund SwiftUIsdirectTouchSystemGesturesDisabled(). Alle drei geben 404 zurück, und keiner der beiden neuen Artikel erwähnt sie. Die Artikel beschreiben, wie eine Systemgeste Ihren Recognizer abbricht; wie man die Systemgeste abschaltet, beschreiben sie nicht. - Ein unter macOS verfügbares ActivityKit. Die Framework-Seite trägt einen Stempel vom 8. Oktober und führt keine macOS-Plattform, und
AlertConfiguration.announcement(_:)aus dem Live-Activities-Talk gibt 404 zurück. Der Abschnitt des Nachmittagsbeitrags zu Live Activities gilt unverändert, und der Beitrag zur Zustandsmaschine bleibt eine reine iOS-Implementierung. - Der „gesture modernization skill“, den der AppKit-Talk bei 12:16 erwähnte. Keine Seite von Apple nennt ihn; der Kommentar
TK skillauf der Landingpage steht noch da.59 - Ein Link von der Talk-Seite zum Artikel sowie jede Aussage von Apple zu einem Weg über Simulator oder Device Hub für Touch auf dem Mac.
- Ein datierter Eintrag auf der Seite mit den Dokumentations-Updates zu SwiftUI. Ihr Serverstempel lautet 2:58 p.m. PT am 28. Oktober, doch ihr neuester Abschnitt ist weiterhin September 2026, und ihr Eintrag zu Gesten ist die Zeile zu
GestureInputKindsvom Juni 2026; was auch immer sich geändert hat, ein neuer datierter Abschnitt war es nicht.6
Korrekturen am Entwicklerbeitrag
Der Nachmittagsbeitrag war zum Stand der Prüfungen um 12 Uhr mittags korrekt; fünf seiner Aussagen hielten bei der Prüfung um 9:51 p.m. PT am 28. Oktober nicht mehr. Ein Update-Block in jenem Beitrag, zusammen mit diesem veröffentlicht, verlinkt hierher, und die Aussagen sind dort an Ort und Stelle korrigiert; dies ist die Liste.
- Aus den drei Kacheln mit „Coming soon“ auf der Landingpage ist eine geworden. Dokumentations- und Designressourcen sind online; die Xcode-27.3-Beta ist es nicht.5
- „Adopting gesture recognizers in your app“ gibt nicht mehr 404 zurück. Der Artikel ist online, und aus ihm sollte man die Migrationshinweise lesen.1
- Die HIG-Seiten schweigen nicht mehr zu Touch auf dem Mac-Display. Drei der vier tragen Zeilen im Änderungsprotokoll vom 28. Oktober; die Live-Activities-Seite nicht.4
- Die Emulationsliste hat fünf Regeln, nicht die vier der Technote vom Juni; die Regel zum Halten und dann Ziehen ist neu hinzugekommen.18
- Exklusive Views sind nur für Apps standardmäßig aktiv, deren Info.plist der Schlüssel fehlt. Xcodes Templates macOS App und Document App schreiben in Xcode 27.1 und der 27.2-Beta den Schlüssel als
NOfür ein Projekt, dessen App-Lebenszyklus der AppKit-App-Delegate ist.127
Was der Nachmittagsbeitrag über die SDKs, die drei undokumentierten Schalter und ActivityKit sagte, bleibt von allem, was seither veröffentlicht wurde, unberührt.
Die wichtigsten Erkenntnisse
Für AppKit-Entwickler: Führen Sie den vierzeiligen Test des Artikels mit Sidecar aus und suchen Sie dann nach den fünf Symbolen, die er nennt; ein Override von mouseDown(with:), mouseDragged(with:) oder mouseUp(with:) in einer Control-Unterklasse wirft dieses Bedienelement auf eine Tracking-Schleife zurück, ob er super aufruft oder nicht, und die Lösung sind Target-Action-Paare für NSControl.Events mit .trackingEndedOutside in der Menge zum Ausblenden.1
Für eine Schleife, die Sie behalten: Der reine Touch-Recognizer braucht buttonMask 0, sonst startet ein Ziehen mit der Maus den Recognizer, und Ihr mouseDown(with:) läuft nie; allowedTouchTypes = .direct ist bereits der Standard.1
Für eigene Recognizer: Erreichen Sie einen Endzustand, überschreiben Sie reset() und location(in:), und debuggen Sie mit -NSGestureRecognizerCrashOnMissingOverrides YES und -NSCrashOnStuckGestureTimeout 2, die vor der Auslieferung entfernt werden; im Produktivbetrieb wird ein hängender Recognizer zwei Sekunden nachdem die Person die Interaktion beendet hat abgebrochen.12
Für die Exklusivität: Eine App ohne den Schlüssel ist exklusiv; ein Projekt aus Xcodes Template macOS App mit dem Lebenszyklus des AppKit-App-Delegate (jedes Storyboard- oder XIB-Projekt) ist es nicht, eines mit dem standardmäßigen SwiftUI-Lebenszyklus schon. Lesen Sie Ihre Info.plist, bevor Sie eine Tracking-Schleife migrieren.27
Für Spiele: Lesen Sie Positionen aus den Events, statt Deltas aufzusummieren, überschreiben Sie sendEvent(_:) nicht, und rechnen Sie damit, dass jede Touch-Oberfläche eine zusätzliche emulierte GCMouse ist, bis NSIsTouchNative auf YES steht.3
Für Designer: Touch ist „a secondary input mode“; Layout und Größe der Bedienelemente bleiben; vergrößern Sie Tap-Ziele, nicht Bedienelemente; blenden Sie verborgene Bedienelemente bei einem Tippen in ihrer Nähe ein und wieder aus, wenn die Person fertig ist oder nach ein paar Sekunden ohne Nutzung, mit Seitenleisten und dem Schließen-Button eines Tabs als Ausnahmen, und geben Sie den essenziellen ein Kontextmenü.4
Häufige Fragen
Ist der AppKit-Migrationsartikel online?
Ja. „Adopting gesture recognizers in your app“ gab am 28. Oktober um 12 Uhr mittags PT 404 zurück und war um 9:51 p.m. online, aufgeführt in den Abschnitten Essentials und Gestures von AppKit und von der Dokumentationskachel der Landingpage verlinkt.15
Sind exklusive Views in meiner App aktiv?
Wenn Ihre Info.plist keinen Schlüssel NSViewGestureRecognizerIsExclusive hat, ja: „If your app’s information property list doesn’t include the NSViewGestureRecognizerIsExclusive key, the system treats it as YES.“2 Ein Projekt, das in Xcode 27.1 oder der 27.2-Beta aus dem Template macOS App mit dem Lebenszyklus des AppKit-App-Delegate erstellt wird (jedes Storyboard- oder XIB-Projekt), bekommt den Schlüssel als Build-Einstellung mit dem Wert NO; dasselbe Template mit dem standardmäßigen SwiftUI-Lebenszyklus schreibt keinen Schlüssel (Analyse des Autors anhand der Templates auf diesem Mac).7
Was passiert mit einem Gesture Recognizer, der nie zum Abschluss kommt?
Er hält seine exklusive View fest, bis das System ihn abbricht: Ein Recognizer „is stuck if it hasn’t reached a terminal state within 2 seconds after the person stops interacting with your app“, und das System „automatically transitions it to the NSGestureRecognizer.State.cancelled state“.2 Setzen Sie beim Debuggen den Default NSCrashOnStuckGestureTimeout, um stattdessen einen Stack-Trace zu bekommen.2
Warum bekommt meine Tracking-Schleife keine Ziehbewegungen mehr, seit ich einen Pan-Recognizer hinzugefügt habe?
Weil buttonMask des Recognizers auf dem Standardwert steht und die primäre Maustaste beansprucht: „A mouse drag then begins the gesture recognizer, and AppKit doesn’t call your mouseDown(with:) override, so the tracking loop doesn’t run.“ Setzen Sie buttonMask für einen reinen Touch-Recognizer auf 0.1
Wie empfängt ein Spiel Touch auf dem Mac?
Als emulierte Maus-Events, in einer dokumentierten Reihenfolge, mit einem mouseMoved(with:) zur neuen Position vor jedem mouseDown(with:), und als zusätzliche GCMouse, deren Deltas nicht stetig sind. Wird NSIsTouchNative auf YES gesetzt, wechselt das Spiel zu Gesture Recognizern, und die emulierte GCMouse entfällt.3
Nennt die HIG eine Größe für Touch-Ziele auf dem Mac?
Nein. Die Seite „Gestures“ sagt „Make tap targets bigger, not the controls themselves“ und überlässt die Größe den Framework-Bedienelementen; die Zahl von 36 Punkten steht in den beiden Talks, nicht in der HIG.49
Woher stammen die Fakten in diesem Beitrag?
Aus Apples zwei AppKit-Artikeln, TN3216, den drei HIG-Seiten und der Landingpage, alle abgerufen am 28. Oktober 2026 um 9:51 p.m. PT, wobei die Verfügbarkeit des Artikels, die HIG-Seiten und die Landingpage mit Prüfungen um 12 Uhr mittags und um 11:49 a.m. am selben Tag verglichen wurden; aus Apples Dokumentations-JSON für jedes genannte Symbol; aus der Technote vom Juni für den Vergleich; aus den Untertitelspuren der Tech Talks für die Zitate aus den Talks; und aus den Headern und Projekt-Templates in Xcode 27.1 und der Xcode-27.2-Beta auf meinem Mac, was Analyse des Autors ist. Presseberichte sind keine Quelle.
Quellen
-
Apple Developer Documentation, Adopting gesture recognizers in your app, AppKit. Abgerufen am 28. Oktober 2026 um 9:51 p.m. PT (HTTP 200,
Last-Modified: Wed, 28 Oct 2026 21:30:45 GMT); dieselbe URL und ihr JSON-Endpunkt gaben am selben Tag um 12 Uhr mittags PT 404 zurück. Aufgeführt im Abschnitt Essentials der AppKit-Framework-Seite, an zweiter Stelle nach „Adopting Liquid Glass“, und unter „Gesture handling“ auf Gestures. Die erste Erfassung der Wayback Machine stammt von 2026-10-29 01:08:12 UTC (CDX-Zeile20261029010812, Status 200; die Belege aus Roh-Header und CDX sind mit den Nachweisen des Beitrags gespeichert). Zitierte Abschnitte: Overview, „Prepare your app for a Mac with a touch display“ (die Tabelle, der vierzeilige Test, die Suchliste), „Filter gesture recognizer candidates“, „Handle touch input“, „Update your event-handling code“, „Replace common mouse-down overrides“, „Handle tracking-loop compatibility“, „Replace your custom tracking loops with gesture recognizers“, „Add touch support to your custom tracking loop“, „Write a custom gesture recognizer“, „Account for gesture recognizer behavior changes“ und „Support non-AppKit UI frameworks“. Das Swift-Listing in diesem Beitrag ist eine vierzeilige Verkürzung des Listings aus „Add touch support to your custom tracking loop“. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple Developer Documentation, Handling AppKit gestures, AppKit. Abgerufen am 28. Oktober 2026 um 9:51 p.m. PT (
Last-Modified: Wed, 28 Oct 2026 05:35:41 GMT); kein Revisionsverlauf auf der Seite; nicht von TN3212 verlinkt; zu diesem Zeitpunkt keine Erfassung im Index der Wayback Machine. Zitierte Abschnitte: Overview, „Track gesture recognizer states“, „Understand gesture recognizer gathering“, „Implement a custom gesture recognizer“, „Prioritize gesture recognizers“, „Delay responder events“, „Configure exclusive views“, „Replace event-tracking loops“ und „Diagnose unresponsive gesture recognizers“. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple Developer Documentation, TN3216: Supporting touch input for games on macOS, Revisionsverlauf „2026-10-28 First published“, abgerufen am 28. Oktober 2026 um 9:51 p.m. PT (
Last-Modified: Wed, 28 Oct 2026 21:09:50 GMT). Zitierte Abschnitte: Overview, „Extend the player experience meaningfully“, „Understand automatic touch support in existing games“, „Understand GCMouse emulation“, „Opt out of mouse emulation“, „Implement touch events“, „Route gestures in your game engine“, „Use standard AppKit controls for automatic touch support“ und „Review your touch integration“. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple Developer, Human Interface Guidelines, abgerufen am 28. Oktober 2026 um 9:51 p.m. PT und verglichen mit Fassungen, die am selben Tag um 12 Uhr mittags PT abgerufen wurden: Designing for macOS, Änderungsprotokoll „October 28, 2026 | Added guidance for touch input.“; Gestures, Änderungsprotokoll „October 28, 2026 | Added guidance for gestures and touch input in macOS.“, mit dem macOS-Plattformabschnitt und der Tabelle „macOS trackpad and touch display gestures“; Pointing devices, Änderungsprotokoll „October 28, 2026 | Added guidance for revealing hidden controls with touch on Mac, and updated guidance and artwork for iPadOS and macOS.“, mit dem Abschnitt „Touch support for hidden controls“; Live Activities, byteidentisch mit der Mittagsfassung, Änderungsprotokoll endet am 13. Oktober 2026. Die Mittagsfassungen der ersten drei enthalten keinen der zitierten Texte. Der
Last-Modified-Header des Servers auf dem neuen Gestures-JSON lautetTue, 27 Oct 2026 05:11:02 GMT, mitX-Cache: hit-stalebei der ersten Anfrage um 9:59 p.m. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple Developer, Get ready for Touch on Mac, abgerufen am 28. Oktober 2026 um 11:49 a.m. PT (drei Kacheln mit „Coming soon“, Links in HTML-Kommentaren) und um 9:51 p.m. PT: „Xcode 27.3 beta“ / „Download the latest SDKs to build, run, and test your app.“ / „Coming soon“; „Documentation resources“ / „Learn how to build and optimize your app for touch input.“ mit Links auf den Artikel und das Beispiel; „Design resources“ / „Create experiences that take advantage of touch user interface patterns.“ mit Links auf Gestures, Designing for macOS, Pointing devices und Apple Design Resources, deren Liste der Produktrahmen „MacBook Pro PROLED XDR Display“ enthält; der Kommentar
TK skillin beiden Abrufen vorhanden. Latest News RSS, neuester Eintrag „Say hello to Touch on Mac“, Wed, 28 Oct 2026 10:00:55 PDT; Releases RSS, neueste Einträge datiert Mon, 26 Oct 2026 10:00:00 PDT (iOS, iPadOS, macOS 27.2 (26B123), tvOS, visionOS und watchOS 27.2); die Liste der Tech Talks mit 111456, 111467 und 111468 und keinem weiteren Talk zu Touch oder Live Activities; die Seite zu Bring Touch to your Mac app, ohne Link auf einen der beiden Artikel; alle abgerufen um 9:51 p.m. PT. ↩↩↩↩↩↩↩↩↩ -
Apples Dokumentations-JSON (
developer.apple.com/tutorials/data/documentation/<path>.json), abgerufen am 28. Oktober 2026 um 9:51 p.m. PT. Gefunden:trackingEndedOutside(macOS 11.0),NSEvent.isTouchSwipeNavigationEnabled(macOS 27.0),NSTextSelectionManager.gesturesForFailureRequirements(macOS 27.0),gestureRecognizer(_:shouldRequireFailureOf:)an NSGestureRecognizerDelegate (macOS 10.10),NSViewGestureRecognizerIsExclusive(„The default value isYES.“),NSIsTouchNative, Mouse, Keyboard, and Trackpad (die beiden Sätze zur Responder Chain und zu Touch), Supporting drag and drop through file promises (der Satz zu direkten Berührungen) und SwiftUI updates (Last-Modified: Wed, 28 Oct 2026 21:58:06 GMT, neuester Abschnitt „September 2026“). Nicht gefunden (HTTP 404):appkit/nswindow/disablesdirecttouchsystemgestures,uikit/uiviewcontroller/prefersdirecttouchsystemgesturesdisabled,swiftui/view/directtouchsystemgesturesdisabled(),activitykit/alertconfiguration/announcement(_:),xcode-release-notes/xcode-27_3-release-notes,macos-release-notes/macos-27_3-release-notes. ActivityKit:Last-Modified: Thu, 08 Oct 2026 15:31:35 GMT, Plattformen iOS, iPadOS und Mac Catalyst. ↩↩↩↩↩↩↩↩↩↩↩ -
SDK- und Template-Prüfung am 28. Oktober 2026 (Analyse des Autors), in
MacOSX27.0.sdkin Xcode 27.1 (27A9275): InNSControl.hlautet Zeile 180NSControlEventTrackingEndedOutside = 1 << 7, mit derselben Zeile inMacOSX27.2.sdkin der Xcode-27.2-Beta (27B5019j);NSEvent.hZeile 496 lautet@property (class, readonly, getter=isTouchSwipeNavigationEnabled) BOOL touchSwipeNavigationEnabled API_AVAILABLE(macos(27));;NSTextSelectionManager.hZeile 59 deklariertgesturesForFailureRequirements;NSGestureRecognizer.hZeilen 116 und 149 deklarierenshouldRequireFailureOfGestureRecognizer:. Projekt-Templates, mit plistlib gelesen, in Xcode 27.1 (27A9275) und der Xcode-27.2-Beta (27B5019j), unterPlatforms/MacOSX.platform/Developer/Library/Xcode/Templates/Project Templates/macOS/Application/:Cocoa App Base.xctemplate/TemplateInfo.plistenthält den SchlüsselINFOPLIST_KEY_NSViewGestureRecognizerIsExclusivemit dem String-WertNOunterOptions[0] (appLifecycle) / Units / Cocoa / Targets[0] / SharedSettingsund nirgends sonst;macOS App Lifecycle.xctemplateunterProject Templates/macOS/Base/definiert die OptionappLifecycleohneName, mitTypepopup,DefaultSwiftUIundValuesSwiftUI,Cocoa;macOS Application UI.xctemplatedefiniertuserInterfacemitNameInterface:,DefaultSwiftUIundValuesSwiftUI,Storyboard,XIB, und seine EinheitenStoryboardundXIBtragen, wie eine seinerSwiftUI-Einheiten,RequiredOptionsmitappLifecycleCocoa;App.xctemplatestammt überCore Data App,macOS SwiftUI App,macOS Application UIundCocoa App Native BasevonCocoa App Baseab;Cocoa Document App.xctemplateschreibt einen KnotenInfo.plist:NSViewGestureRecognizerIsExclusive, dessen Definition<key>NSViewGestureRecognizerIsExclusive</key><false/>lautet, innerhalb seiner Lebenszyklus-EinheitCocoa;Game.xctemplatesetztappLifecycleaufOverrideCocoa;Document App.xctemplatestammt sowohl vonCocoa Document Appals auch vonCocoa App Baseab (überCore Data Document App,macOS SwiftUI Document-Based App,macOS Application UIundCocoa App Native Base). Nur diese beiden Template-Dateien enthalten den Schlüssel, in beiden Xcode-Versionen. XcodesINFOPLIST_KEY_-Build-Einstellungen werden in die generierte Info.plist geschrieben. ↩↩↩↩↩↩↩↩↩↩ -
Apple Developer Documentation, TN3212: Adopting gesture recognizers for Sidecar touch support, Revisionsverlauf „2026-06-08 First published“, Abschnitt „Support third-party UI frameworks“, vier Punkte: „A tap emulates a mouse-down followed immediately by a mouse-up“, „A touch and immediate pan emulates trackpad scroll events, even if the UI doesn’t respond to them“, „A long press without movement emulates a rightMouseDown(with:) (optionally followed by rightMouseDragged(with:)) with a rightMouseUp(with:) on touch lift“ und „A two-finger pinch or rotation simultaneously emulates both the trackpad magnify and rotate gestures“. Abgerufen am 28. Oktober 2026. ↩↩↩
-
Apple Developer, Tech Talks Bring Touch to your Mac app (13:19, Raleigh Ledet) und Design for touch on Mac (16:52, Cas Lemmens), 28. Oktober 2026. Die Zitate stammen aus den englischen Untertitelspuren; die Zeit in Klammern ist der erste Cue des zitierten Abschnitts: „drag with a small hold delay“ (111468, 11:31), „there’s a gesture modernization skill that has an understanding of the features and changes I’ve talked about“ (111468, 12:16), „Touch on macOS uses a touch target of 36 points“ (111456, 9:17), „a minimum of 36 points on each axis“ (111468, 5:53). Das Code-Listing der Talk-Seite „Use NSControl events to detect tracking“ (2:24) registriert
addTarget(_:action:for:)für[.trackingBegan, .trackingEntered],[.trackingExited, .trackingEndedInside]und.trackingCancelled. ↩↩↩↩↩