El 28 de octubre Apple presentó el MacBook Pro con PROLED XDR Display, el primer Mac con pantalla táctil, con macOS 27 Golden Gate. Para quien desarrolla, la frase del anuncio sobre el MacBook Pro con pantalla táctil es que muchas apps ya existentes funcionan con Touch desde el primer día, porque macOS traduce automáticamente los toques en clics. Para botones, tablas y gestos de SwiftUI es cierto. Para una vista propia con un bucle de seguimiento dentro de mouseDown(with:) lo es a medias. Esa vista recibe un ratón emulado: un clic, un desplazamiento, un clic derecho con una pulsación larga. Nunca ve el dedo en sí, y no tiene forma de echarse atrás cuando el sistema se queda con el gesto.
Apple publicó tres Tech Talks y una nota técnica, la TN3212. Esto es lo que significan para tu código si trabajas con un MacBook Pro con pantalla táctil, y por dónde empezar. Una aclaración para no mezclar los dos anuncios: el MacBook Pro de 14 pulgadas con M6 del 27 de octubre no tiene pantalla táctil. Touch es solo de los modelos de 14 y 16 pulgadas con PROLED XDR Display y M5 Pro o M5 Max, con reservas desde el 28 de octubre y entrega desde el 6 de noviembre.
MacBook Pro pantalla táctil: qué funciona sin cambios#
Apple llama "touch native" a botones, controles, tablas, colecciones, split views, pestañas y desplazamiento: AppKit los construye sobre gesture recognizers, así que un dedo se comporta como un clic. Los gestos existentes de SwiftUI y Mac Catalyst "just work", según la charla.
Lo demás cae en la emulación de ratón. La TN3212 la describe para frameworks de UI de terceros y para código mouseDown(with:) escrito a mano:
| Toque | Qué recibe tu app |
|---|---|
| Tap | mouseDown y luego mouseUp |
| Toque con desplazamiento inmediato | eventos de scroll como los del trackpad |
| Pulsación larga sin movimiento | rightMouseDown, rightMouseDragged si se mueve, rightMouseUp al levantar |
| Pellizco o rotación con dos dedos | magnify y rotate a la vez |
La charla añade que un dedo desplaza con inercia y que el arrastre empieza tras una breve espera. Un dato de diseño importa: macOS en un equipo táctil no se ve distinto. Los tamaños de ventanas, texto y controles no cambian, y el toque sigue siendo una entrada secundaria.
Las apps Catalyst distinguen ratón y toque con los viejos UITouch.touchType y UIGestureRecognizer.allowedTouchTypes. macOS 27 corrige además una carencia de Catalyst: las vistas con UIDragInteraction y UIContextMenuInteraction ahora pueden activar ambas con el dedo.
El toque directo solo llega por gesture recognizers#
La frase del ingeniero de AppKit Raleigh Ledet: "Gesture recognizers are the only way to get direct touches on macOS". La TN3212 detalla las consecuencias. Los event monitors locales y globales no reciben toques, estos no pasan por NSApp ni por sendEvent(_:), y tampoco por la responder chain. El teclado y la rueda de scroll siguen usándola.
El ejemplo mínimo de Apple es un reconocedor de arrastre (pan) que solo acepta un dedo:
let touchRecognizer = NSPanGestureRecognizer(target: self, action: #selector(touchPan))
touchRecognizer.buttonMask = 0x0
touchRecognizer.allowedTouchTypes = .direct
wiringPanelView.addGestureRecognizer(touchRecognizer)allowedTouchTypes es un NSTouch.TouchTypeMask que existe desde macOS 10.12.2. No es nuevo; lo nuevo es que ahora es la única puerta.
Dos trampas de la TN3212. Los reconocedores solo se añaden a vistas, así que para recibir toques en un view controller, añádelo a su view. Y AppKit reúne candidatos subiendo desde la vista superior que devuelve el hit-test, de modo que una vista transparente superpuesta bloquea los reconocedores, incluidos los de los controles estándar. Sobrescribe hitTest(_:) y devuelve nil en esa vista.
Puedes dejar los bucles de seguimiento y añadir al lado reconocedores solo táctiles. Apple lo llama "the fastest and least risky path for a dot release". Para una versión mayor propone separar el seguimiento en funciones start, continue, end y cancel a las que llamen ambos caminos.
Cambia el seguimiento de mouseDown por eventos de NSControl#
Para tus propios botones y deslizadores, la vía de Apple es suscribirse a NSControl.Events con addTarget(_:action:for:) en lugar de hacer un bucle en mouseDown. El ejemplo de la charla:
ignitionButton.addTarget(self, action: #selector(buttonDidStartTracking), for: [.trackingBegan, .trackingEntered])
ignitionButton.addTarget(self, action: #selector(buttonDidEndTracking), for: [.trackingExited, .trackingEndedInside])
ignitionButton.addTarget(self, action: #selector(buttonCancelledTracking), for: .trackingCancelled)La tercera línea no tiene equivalente en el mundo del ratón. trackingCancelled se dispara cuando un gesto del sistema, como Exposé, o un scroll interrumpe el seguimiento; revierte ahí los cambios a medias. También existen valueChanged, primaryActionTriggered, menuActionTriggered y los agregados allTrackingEvents y allEvents.
Tres avisos. Si heredas de un control estándar y sobrescribes cualquier método del responder para el botón izquierdo del ratón, el control vuelve a un bucle de seguimiento; igual con NSTableView y NSCollectionView, donde Apple remite a los métodos del delegado. Tras la migración, NSApplication.currentEvent ya no devuelve el evento que provocó la acción: lee NSEvent.modifierFlags y NSEvent.pressedMouseButtons, y ten en cuenta que un toque no aparece en pressedMouseButtons. Y las fechas se enredan un poco: la TN3212 indica como nuevos en macOS 27 trackingRepeated, valueChanged, primaryActionTriggered, menuActionTriggered y applicationReserved, mientras que la documentación fecha NSControl.Events en macOS 11. Comprueba la disponibilidad miembro por miembro.
Varios gestos a la vez#
Por defecto solo una jerarquía de vistas puede tener reconocedores activos. Imita un bucle de seguimiento modal, y el scroll es la excepción. Para paneles donde los dedos mueven varias cosas a la vez (la charla de diseño usa dos deslizadores en Logic) se desactiva:
stoveControlsView.exclusiveGestureBehavior = .notExclusiveNSView.exclusiveGestureBehavior (macOS 27.0) admite .inherit por defecto, .exclusive o .notExclusive, y las subvistas lo heredan. El panel de color estándar de AppKit ya deja mover varios deslizadores juntos. Tres claves de Info.plist, todas de macOS 27.0:
| Clave | Tipo | Efecto |
|---|---|---|
NSViewGestureRecognizerIsExclusive | Boolean, por defecto YES | valor por defecto para toda la app; NO permite reconocedores activos en varias vistas |
NSGestureRecognizerSuppressesMainMenuActions | Boolean | si se ignoran los atajos de menú durante un gesto no exclusivo; los exclusivos siempre los suprimen |
NSIsTouchNative | Boolean | evita que AppKit inyecte reconocedores de emulación de ratón en apps que integran un framework de UI que no es AppKit; Apple: "Most AppKit apps don't need this key" |
Si ninguna pantalla de tu app necesita dos gestos simultáneos, no toques nada de esto.
Zonas de toque: 36 pt en la charla, 44 pt en la HIG#
Cas Lemmens, del equipo de diseño de Apple, da la cifra: "Touch on macOS uses a touch target of 36 points", el mismo tamaño físico en pantalla que 44 puntos en iOS. Los controles del framework reciben una zona táctil invisible de 36 pt y conservan su tamaño visual. Hay una discrepancia que conviene conocer: la página Buttons de la HIG publicada aún pide una región de al menos 44x44 pt. Los 36 pt salen de la Tech Talk de diseño y de un comentario en el código de ejemplo de Apple, no de las guías.
En controles propios, amplía la zona táctil, no el control, y solo en el camino del toque. En el ejemplo de Apple la lógica expandHitRect se ejecuta desde el reconocedor táctil, así que el ratón sigue siendo preciso. Las filas de FormStyle miden 36 pt por defecto, los inspectores mantienen controles medianos y los pequeños y mini siguen "fully support touch".
El hover no tiene equivalente táctil. Los tooltips no tienen que funcionar con el toque, y los botones de scroll que aparecen al pasar el cursor siguen ocultos durante el toque. Toda acción esencial que solo aparezca con hover necesita otra vía, por ejemplo un menú contextual. La opción de Apple en AppKit es un reconocedor de clic solo táctil que muestra el elemento temporalmente con un tap.
Scroll views: dos dedos para lienzos de dibujo#
macOS 27 añade a NSScrollView isTouchScrollingEnabled (por defecto true), minimumNumberOfTouchesForScrolling (por defecto 1), maximumNumberOfTouchesForScrolling y scrollGestureForRelationships, este último para relaciones de fallo y exclusión frente al scroll. Para un lienzo donde un dedo dibuja y aun así hay que poder desplazar, el ejemplo de Apple es una línea:
scrollView.minimumNumberOfTouchesForScrolling = 2También hay NSRefreshController para pull-to-refresh, que se conecta con NSScrollView.refreshController y tiene target/action, beginRefreshing() y endRefreshing(). Un reconocedor propio que deba ceder ante el scroll puede activar isCancellableByScrollGesture, que es false por defecto.
En SwiftUI, nombra la fuente de entrada#
GestureInputKinds (macOS 27.0) es un option set: .directTouch, .indirectTouch (multitáctil del trackpad, Siri Remote), .pointer, .pencil y .all. Hay inicializadores con inputKinds en DragGesture, LongPressGesture, MagnifyGesture, RotateGesture, SpatialTapGesture, TapGesture y otros. Un lienzo donde el dedo dibuja y el ratón selecciona:
let draw = DragGesture(minimumDistance: 0, coordinateSpace: .local, inputKinds: .directTouch)
let select = DragGesture(minimumDistance: 4, coordinateSpace: .local, inputKinds: .pointer)Si ya tienes un NSGestureRecognizer propio, NSGestureRecognizerRepresentable lo envuelve para SwiftUI sin reescribirlo. Sus métodos son makeNSGestureRecognizer(context:), updateNSGestureRecognizer(_:context:), handleNSGestureRecognizerAction(_:context:) y makeCoordinator(converter:), y se añade con .gesture(_:). La documentación lo data en macOS 26.0 mientras WWDC26 lo llama nuevo; "disponible" es la palabra más segura.
Los gestos del sistema se llevan los dedos primero#
La charla de diseño enumera los gestos de la pantalla: tres dedos hacia arriba, Mission Control; hacia abajo, Exposé de apps; a izquierda y derecha, cambio de Spaces; pulgar más tres dedos en pellizco, apps; abriéndolos, escritorio. Deslizar hacia abajo desde el borde superior muestra la barra de menús oculta; hacia arriba desde el inferior, el Dock; hacia la izquierda desde el borde derecho, el Centro de notificaciones; hacia abajo desde la esquina superior derecha, el Centro de control. Deslizar sobre la Dynamic Island llama a Siri. Según el comunicado, un doble toque en una ventana la lleva a pantalla completa y un deslizamiento rápido la coloca en mosaico.
El detalle práctico: mover tres o más deslizadores en la misma dirección a la vez activa un gesto del sistema. Para las pocas ventanas donde eso estorba, el código de ejemplo de Apple muestra tres formas de desactivarlo: NSWindow.disablesDirectTouchSystemGestures, UIViewController.prefersDirectTouchSystemGesturesDisabled en Catalyst y View.directTouchSystemGesturesDisabled() en SwiftUI. Aparecen en el código de ejemplo y todavía no tienen página de documentación. El consejo de Apple: desactivar "only if the interaction capabilities they open up are critical for the window", y mejor solo durante el momento crítico.
Pantalla y Dynamic Island, en breve#
ProMotion funciona de forma adaptativa de 10 a 120 Hz. El brillo llega a 1000 nits sostenidos a pantalla completa y 2000 nits de pico (exterior en SDR, contenido HDR en XDR). Las API de EDR no cambian, así que NSScreen.maximumExtendedDynamicRangeColorComponentValue y CAMetalLayer.wantsExtendedDynamicRangeContent funcionan como antes. La documentación no muestra una API nueva de geometría de la Dynamic Island para AppKit; siguen safeAreaInsets y auxiliaryTopLeftArea. Sobre el burn-in, Apple solo dice que el motor de pantalla compensa el desgaste de los píxeles y no da pautas para desarrolladores.
Sobre Live Activities en Mac, la Tech Talk dice que las apps universales con actividades existentes funcionan sin cambiar código y que las apps nativas de macOS pueden crearlas directamente. Aparecen en el borde superior en un Mac con Dynamic Island y en la barra de menús en los demás. La documentación de ActivityKit todavía no lista macOS, y alertConfiguration: .announcement(...) del ejemplo de Apple no aparece entre los miembros de AlertConfiguration. Espera a la documentación antes de apoyarte en ello.
Cómo probar sin el Mac nuevo#
Apple: con la última beta de macOS y las herramientas de desarrollo, todas las funciones táctiles están disponibles en un iPad conectado por Sidecar. En macOS 27 los toques de un iPad con iPadOS 27 por Sidecar también llegan solo mediante gesture recognizers.
Cuidado con NSScreen.touchCapabilities (.multiTouch): con una pantalla Sidecar táctil conectada devuelve true para todas las pantallas. Apple aconseja gestionar la entrada sin importar la fuente en lugar de decidir según este valor.
Dos defaults de depuración: NSCrashOnStuckGestureTimeout provoca un crash de la app cuando un gesto exclusivo atascado se cancela solo unos segundos después de terminar la entrada, y NSGestureRecognizerCrashOnMissingOverrides señala subclases que no tienen los overrides requeridos. Una trampa aparece al recompilar: en apps compiladas con el SDK de macOS 27, la implementación base de NSGestureRecognizer.location(in:) devuelve NSZeroPoint y registra un error si tu subclase no la sobrescribe. Los reconocedores propios deben llegar a .ended, .failed o .cancelled, o bloquean a los demás; implementa mouseCancelled(with:) y touchesCancelled(with:).
Todos los símbolos nuevos son de macOS 27.0, así que envuelve las llamadas en #available(macOS 27, *) si sigues dando soporte a sistemas anteriores.
Lista de la primera semana#
- Recompila con el SDK de macOS 27 y busca subclases de
NSGestureRecognizersin override delocation(in:). - Activa
NSCrashOnStuckGestureTimeoutyNSGestureRecognizerCrashOnMissingOverridesen el esquema de depuración. - Conecta un iPad por Sidecar y recorre las pantallas principales con el dedo: tap, arrastre, pulsación larga, scroll.
- Localiza el seguimiento propio en
mouseDowny añade al lado un reconocedor solo táctil conallowedTouchTypes = .direct. - Pasa el seguimiento de tus controles a
NSControl.Eventsy gestionatrackingCancelled. - Amplía a 36 pt las zonas pequeñas de controles propios, solo en el camino táctil.
- Busca acciones solo con hover y dales una segunda vía.
- Revisa las ventanas con gestos simultáneos y decide si necesitas
.notExclusive.
Lo que Apple aún no ha explicado#
El portal Touch on Mac en developer.apple.com/touch-on-mac/ parece inacabado el día del anuncio. "Documentation resources" y "Design resources" dicen "Coming soon", igual que la beta de Xcode 27.3. El artículo "Adopting gesture recognizers in your app" está comentado en el HTML y devuelve 404. La HIG no tiene página de touch en Mac, y su página Gestures sigue diciendo que los usuarios de Mac dependen del teclado, el ratón y el trackpad. Las API para desactivar gestos del sistema no están documentadas, y la disponibilidad de Live Activities para apps nativas de macOS no consta en la documentación. La charla menciona un "gesture modernization skill" en las herramientas de desarrollo que todavía no se encuentra. Las dudas se llevan a los group labs del 18 de noviembre (9 a 10 h PT) y el 19 de noviembre (18 a 19 h PT).
La reacción en Hacker News gira sobre todo en torno a las huellas en la pantalla, el cansancio del brazo y la falta de modo tableta, mientras quienes desarrollan para móvil valoran probar gestos de iOS directamente en la pantalla del Mac. Nada de eso cambia la lista de arriba, pero coincide con la postura de diseño de Apple: el toque es secundario y los gestos deben funcionar también en el trackpad y en un menú.
Si actualizas tu interfaz para los materiales nuevos, compara con la adopción de Liquid Glass en SwiftUI. Una historia parecida de adaptación a hardware nuevo es la guía de adaptación a iPhone Duo, y a las apps de barra de menús les puede servir el artículo sobre una app de barra de menús de macOS en SwiftUI. Fuentes primarias: Touch on Mac, TN3212 y las Tech Talks Design for touch on Mac, Bring Touch to your Mac app y Bring Live Activities to Mac.



