wandres.dev
SWIFTUI AVANZADO · @Observable y animaciones

Gestos y dibujo personalizado

Responde al tacto con gestos (tap, drag, magnify) y dibuja lo que imagines con Shapes, Path y Canvas. La interfaz que va más allá de los controles estándar.

⏱ 14 min

Los controles estándar cubren mucho, pero las apps memorables tienen toques propios: una tarjeta que arrastras, un gráfico dibujado a medida, una interacción táctil única. Para eso SwiftUI te da gestos y un lienzo de dibujo.

🎯 Al terminar esta lección sabrás
  • Gestos simples: tap, long press.
  • Arrastrar con DragGesture y @GestureState.
  • Formas listas y Path a medida.
  • Dibujo eficiente con Canvas.

Gestos simples

Los más comunes son modificadores directos:

Text("Tócame")
    .onTapGesture { print("tap") }
    .onLongPressGesture { print("mantenido") }

// tap doble o con posición
Circle()
    .onTapGesture(count: 2) { print("doble tap") }

Arrastrar: DragGesture

Para gestos con estado continuo (arrastrar), usas .gesture(...) con DragGesture. @GestureState guarda el desplazamiento solo mientras dura el gesto:

struct Arrastrable: View {
    @State private var posicion = CGSize.zero
    @GestureState private var arrastre = CGSize.zero

    var body: some View {
        Circle()
            .frame(width: 80, height: 80)
            .offset(x: posicion.width + arrastre.width,
                    y: posicion.height + arrastre.height)
            .gesture(
                DragGesture()
                    .updating($arrastre) { valor, estado, _ in
                        estado = valor.translation      // mientras arrastras
                    }
                    .onEnded { valor in
                        posicion.width += valor.translation.width
                        posicion.height += valor.translation.height
                    }
            )
    }
}
💡
@GestureState se resetea solo

@GestureState vuelve a su valor inicial automáticamente cuando el gesto termina. Por eso el patrón es: acumula el resultado final en un @State normal (posicion) en onEnded, y usa el @GestureState (arrastre) solo para el movimiento en curso. Así el objeto se queda donde lo soltaste.

Formas y Path

SwiftUI trae formas listas: Circle, Rectangle, RoundedRectangle, Capsule, Ellipse. Y para lo tuyo, Path dibuja líneas y curvas:

Path { p in
    p.move(to: CGPoint(x: 0, y: 100))
    p.addLine(to: CGPoint(x: 100, y: 0))
    p.addLine(to: CGPoint(x: 200, y: 100))
    p.closeSubpath()
}
.fill(.orange)

Canvas: dibujo eficiente

Cuando necesitas dibujar muchos elementos (partículas, gráficos, visualizaciones), Canvas es un lienzo de alto rendimiento que dibuja en modo inmediato, sin crear una vista por elemento:

Canvas { context, size in
    for i in 0..<50 {
        let x = Double.random(in: 0...size.width)
        let y = Double.random(in: 0...size.height)
        let punto = Path(ellipseIn: CGRect(x: x, y: y, width: 6, height: 6))
        context.fill(punto, with: .color(.blue))
    }
}
¿Vistas o Canvas?

Para unos pocos elementos interactivos, usa vistas normales (Circle, Rectangle): son componibles y animables. Para cientos o miles de elementos de dibujo (un gráfico denso, un sistema de partículas, un editor de trazos), Canvas es muchísimo más rápido porque no crea una vista SwiftUI por cada píxel: dibuja directo. Elegir la herramienta correcta según la cantidad es lo que separa una interfaz fluida de una que se arrastra.

⚔️ Interacción y dibujo a medida
  1. Añade .onTapGesture a una vista y cambia su color al tocarla.
  2. Haz un círculo arrastrable con DragGesture y @GestureState.
  3. Dibuja un triángulo con Path.
  4. Usa Canvas para dibujar 30 círculos en posiciones aleatorias.