💭 Enable Multi-Finger Tap Gestures in SwiftUI

Get weekly handpicked updates on Swift and SwiftUI!

TL;DR: SwiftUI does not natively support multi-finger taps, but you can implement them using UIKit integrations. For iOS 18+, leverage UIGestureRecognizerRepresentable to bridge UIKit gestures into SwiftUI. For earlier iOS versions, use UIViewRepresentable to wrap UITapGestureRecognizer for multi-finger tap detection.

Background

SwiftUI’s built-in TapGesture supports only single-finger taps and the number of consecutive taps but does not natively handle multi-finger taps. This guide provides solutions for implementing multi-finger tap gestures across different iOS versions.

iOS 18+

Starting in iOS 18, developers can use UIGestureRecognizerRepresentable to bridge UIKit gesture recognizers into SwiftUI, enabling multi-finger taps with ease.

Example Code

Swift
struct TwoFingerTapDemo: View {
  var body: some View {
    Rectangle()
      .foregroundStyle(.orange)
      .frame(width: 200, height: 200)
      .onTapGesture {
        print("Single Finger Tap")
      }
      // Apply TwoFingerTapGesture
      .gesture(TwoFingerTapGesture {
        print("Two-Finger Tap")
      })
  }
}

struct TwoFingerTapGesture: UIGestureRecognizerRepresentable {
  let action: () -> Void

  func makeUIGestureRecognizer(context: Context) -> some UIGestureRecognizer {
    let gesture = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleGesture))
    gesture.numberOfTouchesRequired = 2 // Set required number of fingers
    gesture.delegate = context.coordinator
    return gesture
  }

  func makeCoordinator(converter: CoordinateSpaceConverter) -> Coordinator {
      Coordinator(action: action)
  }

  class Coordinator: NSObject, UIGestureRecognizerDelegate {
    let action: () -> Void

    init(action: @escaping () -> Void) {
      self.action = action
    }

    @objc func handleGesture() {
      action()
    }

    func gestureRecognizer(_: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith _: UIGestureRecognizer) -> Bool {
      true // Allow simultaneous gestures
    }
  }
}

Features

  • Custom Gestures: UIGestureRecognizerRepresentable allows the seamless integration of UIKit gesture recognizers into SwiftUI.
  • Advanced Control: Coordinator provides hooks for managing gesture behavior, like simultaneous gesture recognition.

Supporting Older iOS Versions

For iOS versions below 18, implement multi-finger taps using UIViewRepresentable to wrap UIKit gestures in a SwiftUI-compatible component.

Steps to Implement

  1. Create a UIView for Multi-Finger Tap A custom UIView class with a UITapGestureRecognizer detects multi-finger taps.
  2. Wrap with UIViewRepresentable Expose the custom UIView as a SwiftUI view.
  3. Extend SwiftUI Add a modifier for easier usage.

Example Code

Swift
struct TowFingerTapDemo: View {
    var body: some View {
        Rectangle()
            .foregroundStyle(.orange)
            .frame(width: 200, height: 200)
            .onTwoFingerTap {
                print("Two Touches")
            }
            .onTapGesture {
                print("One Touch")
            }
    }
}

extension View {
    func onTwoFingerTap(perform action: @escaping () -> Void) -> some View {
        overlay(TwoFingerTapLayer(action: action))
    }
}

struct TwoFingerTapLayer: UIViewRepresentable {
    let action: () -> Void

    func makeUIView(context: Context) -> some UIView {
        let view = TwoFingerTapUIView(action: action)
        view.backgroundColor = .clear
        return view
    }

    func updateUIView(_ uiView: Self.UIViewType, context: Self.Context) {}
}

class TwoFingerTapUIView: UIView {
    let action: () -> Void
    private var gesture: UITapGestureRecognizer!

    init(action: @escaping () -> Void) {
        self.action = action
        super.init(frame: .zero)
        setupGesture()
    }

    @available(*, unavailable)
    required init?(coder _: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupGesture() {
        gesture = UITapGestureRecognizer(target: self, action: #selector(handleGesture))
        gesture.numberOfTouchesRequired = 2 // Set required number of fingers
        addGestureRecognizer(gesture)
    }

    @objc private func handleGesture() {
        action()
    }
}

Key Points

  • Gesture Order Matters To avoid conflicts, apply custom gestures like onTwoFingerTap before SwiftUI’s native gestures:

    Swift
    // Correct order
    .onTwoFingerTap { print("Two-Finger Tap") }
    .onTapGesture { print("Single Finger Tap") }

Further Reading