Sample CodeiOS, iPadOS, Mac CatalystReviewed 2026-07-21View on Apple Developer

Supporting gesture interaction in your apps

At a glance

Item Summary
Purpose Combine standard recognizers with a custom gesture-recognizer state machine and simultaneous-recognition policy.
App architecture ViewController coordinates pan, pinch, rotation, and a custom ResetGestureRecognizer; the custom recognizer owns touch history and publishes state transitions back through UIKit target-action.
Main patterns Gesture recognizer state machine, Target-action coordination, Framework delegate policy
Project style Four Swift files; one feature controller and one custom UIGestureRecognizer subclass.

Project structure

Source bundle/
├── GestureRecognizer/
│   ├── AppDelegate.swift
│   ├── SceneDelegate.swift
│   ├── ViewController.swift
│   ├── ResetGestureRecognizer.swift
│   ├── Base.lproj/
│   │   ├── LaunchScreen.storyboard
│   │   └── Main.storyboard
│   └── Info.plist
├── Configuration/
│   └── SampleCode.xcconfig
└── GestureRecognizer.xcodeproj/
    ├── .xcodesamplecode.plist
    └── project.pbxproj

Structure observations

  • ResetGestureRecognizer is the reusable recognition engine; ViewController owns visual piece transforms.
  • Standard gesture actions and the custom recognizer converge on the same controller-owned UI state.
  • The controller’s UIKit delegate methods define coexistence and touch-filtering policy.

Overall architecture

Reference code

GestureRecognizer/ViewController.swift:16 — custom recognizer attachment

    override func viewDidLoad() {
        super.viewDidLoad()
        initializePieces()
        let resetGestureRecognizer = ResetGestureRecognizer(
            target: self,
            action: #selector(resetPieces(_:)))
        view.addGestureRecognizer(resetGestureRecognizer)
        resetGestureRecognizer.delegate = self
        resetGestureRecognizer.cancelsTouchesInView = false
    }

Interpretation

Recognition and presentation are separate. The recognizer reduces raw touches into UIKit states; the controller receives target-action callbacks and applies view transforms. Its delegate conformance decides which recognizers may run together and which touches the reset recognizer receives.

Ownership and state

Ownership evidence

GestureRecognizer/ResetGestureRecognizer.swift:12 — recognizer-local state

class ResetGestureRecognizer: UIGestureRecognizer {

    private var trackedTouch: UITouch?
    private var touchedPoints = [CGPoint]()

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesBegan(touches, with: event)
    }
}
Owner Object or state Relationship Mutation authority
Root UIView ResetGestureRecognizer Retains the recognizer after addGestureRecognizer UIKit drives its touch callbacks and attachment lifetime.
ResetGestureRecognizer Tracked touch and point history Owns private recognition state Only the recognizer resets/appends/interprets it.
ViewController Yellow, blue, and pink piece outlets Receives storyboard-owned weak references The controller mutates transforms, centers, bounds, and z-order.
ViewController Recognizer policy Acts as target and UIKit delegate It allows simultaneous recognition and filters reset touches.

Composition denotes source-visible construction and retained value state. Aggregation denotes a stored or injected collaborator whose exclusive lifetime the source does not prove.

Class and protocol design

GestureRecognizer/ResetGestureRecognizer.swift:59 — recognizer state transition

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesEnded(touches, with: event)
        let count = countHorizontalTurning(touchedPoints: touchedPoints)
        state = count > 2 ? .ended : .failed
    }

    override func reset() {
        super.reset()
        trackedTouch = nil
        touchedPoints.removeAll()
        state = .possible
    }
Type Responsibility Depends on or conforms to
ResetGestureRecognizer Recognizes a path with at least three horizontal turns UIGestureRecognizer
ViewController Coordinates piece transforms and reset animation UIViewController, UIGestureRecognizerDelegate
AppDelegate / SceneDelegate Application and scene lifecycle only UIKit lifecycle protocols

UIGestureRecognizerDelegate is a framework callback seam. There is no local protocol abstraction; reuse comes from the custom recognizer subclass.

Access control

Symbol Access Verified effect Design reason
trackedTouch, touchedPoints private Only ResetGestureRecognizer can access recognition history Protects the state-machine invariant from controller/UI mutation.
countHorizontalTurning private Only the recognizer invokes its detection helper The algorithm is an implementation detail, not a controller capability.
initializePieces, adjustAnchor private Only ViewController uses these UI helpers Keeps transform setup behind public/IBAction event entry points.
Recognizer and controller types implicit internal Visible inside the app target The sample exports no reusable framework API.

Reference code

GestureRecognizer/ResetGestureRecognizer.swift:14 — private recognition state

    private var trackedTouch: UITouch?
    private var touchedPoints = [CGPoint]()

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesMoved(touches, with: event)
        guard state != .failed else { return }
    }

Declarations without an explicit modifier are module-internal. private is used for state and helpers that implement one type’s invariant; no public library surface is inferred for a single app target.

Logic ownership and placement

Logic Owning type or file Why it lives there
Horizontal-turn detection and state transitions ResetGestureRecognizer Recognition depends only on touch history and UIKit state.
Piece layout and transforms ViewController The controller owns all three visual outlets.
Simultaneous/touch filtering policy ViewController delegate methods Policy depends on the screen’s pieces and root view.
Lifecycle AppDelegate and SceneDelegate No feature logic is placed in app/scene callbacks.

Design patterns

Pattern Source evidence Purpose or tradeoff
Gesture recognizer state machine GestureRecognizer/ResetGestureRecognizer.swift:23 Converts an incremental touch stream into standard UIKit recognizer states.
Target-action coordination GestureRecognizer/ViewController.swift:23 The controller reacts to recognition without owning the recognizer algorithm.
Framework delegate policy GestureRecognizer/ViewController.swift:147 Screen-specific coexistence/filtering remains outside the reusable recognizer.

Naming conventions

  • ResetGestureRecognizer names the user intent and UIKit role, not its horizontal-turn implementation.
  • Action methods use gesture verbs (panPiece, pinchPiece, rotatePiece, resetPieces).
  • Boolean/state names follow UIKit vocabulary (cancelsTouchesInView, recognizer state).
  • The custom recognizer has its own file; lifecycle types remain separate.

Architecture takeaways

  • Put raw-touch accumulation and recognition transitions inside the recognizer subclass.
  • Keep view transforms and gesture coexistence policy on the screen controller.
  • Private recognition state prevents callers from forcing inconsistent transitions.

Source map

Source file Architectural role
GestureRecognizer/ResetGestureRecognizer.swift Custom recognition algorithm and state machine
GestureRecognizer/ViewController.swift Recognizer composition, target actions, and delegate policy
GestureRecognizer/AppDelegate.swift / SceneDelegate.swift Application and scene lifecycle