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

Enhancing your iPad app with pointer interactions

At a glance

Item Summary
Purpose Applies custom pointer regions, shapes, and effects to a quilt editor, buttons, and color swatches.
App architecture ViewController coordinates tools, while QuiltView owns stitch state, persistence, drawing, taps, and its editor pointer interaction.
Main patterns Custom view state owner, weak selection delegate, per-component pointer strategies, and view composition.
Project style Storyboard UIKit app with pointer behavior colocated beside the view whose geometry it describes.

Project structure

QuiltSimulator/
├── ViewController.swift
├── QuiltView.swift
├── ThreadPickerViewController.swift
├── PatchworkView.swift
├── ColorBlockView.swift
├── AppDelegate.swift
├── SceneDelegate.swift
└── Base.lproj/Main.storyboard

Structure observations

  • The main screen coordinates menus and tool selection but does not store stitch locations.
  • The thread picker contains its cell because the cell’s lift effect is local to that feature.
  • PatchworkView and ColorBlockView build the decorative background independently from the editable stitch overlay.

Overall architecture

Reference code

QuiltSimulator/QuiltView.swift:62 — the editor view installs both its drawing input and pointer adapter with the state they affect.

func sharedInit() {
    let patchworkView = PatchworkView()
    patchworkView.translatesAutoresizingMaskIntoConstraints = false
    self.insertSubview(patchworkView, at: 0)
    patchworkView.centerXAnchor.constraint(equalTo: self.safeAreaLayoutGuide.centerXAnchor).isActive = true
    patchworkView.centerYAnchor.constraint(equalTo: self.safeAreaLayoutGuide.centerYAnchor).isActive = true

    self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:))))
    stitchLayer.lineWidth = 2
    stitchLayer.fillColor = UIColor.clear.cgColor
    stitchLayer.lineCap = .round
    self.layer.addSublayer(stitchLayer)

    self.addInteraction(UIPointerInteraction(delegate: self))
}

Interpretation

Pointer support is an enhancement of existing components, not a central service. Each component calculates a style from its own bounds and purpose, while the controller only provides button-specific closures and tool routing.

Ownership and state

Ownership evidence

QuiltSimulator/ThreadPickerViewController.swift:10 — selection leaves the picker through a class-bound weak delegate rather than direct knowledge of the main screen.

protocol ThreadPickerViewControllerDelegate: NSObjectProtocol {

    func threadPickerDidPickColor(_ threadPicker: ThreadPickerViewController, color: UIColor)
}
Owner State Relationship Mutation authority
QuiltView Mode, color, stitch points, dash pattern, shape layer Owns editor model/presentation Tap/removal APIs and property setters
ViewController Tool button and screen outlets Coordinates User actions and picker callback
ThreadPickerViewController Color choices Owns fixed picker data Collection selection
ThreadCell Swatch and pointer interaction UIKit owns reused cells Cell configuration/layout

Class and protocol design

Type Responsibility Design note
QuiltView Persist editor preferences, collect stitches, draw path, provide crosshair pointer Cohesive feature view, though model and rendering are combined
ViewController Configure tools/buttons/menus and apply chosen color Screen coordinator
ThreadPickerViewController Present choices and emit one semantic selection event Reusable child flow through a weak delegate
ThreadCell Circular swatch and matching lift preview Cell-local pointer geometry
PatchworkView / ColorBlockView Compose decorative quilt background Composite view hierarchy

ThreadPickerViewControllerDelegate is the only app-defined protocol. It reduces coupling to one UIColor result. Pointer and scroll behavior use UIKit protocols because those callbacks are driven by the framework.

Access control

Symbol Access Verified effect Rationale
User-default key constants private static Only QuiltView can address its persisted settings. Storage keys are implementation details.
Picker delegate implicit internal weak The presenter assigns it without creating a retain cycle. Cross-type collaboration inside the app target.
Stitch state and drawing helpers implicit internal Other target code could access them, although only QuiltView currently mutates most. Sample favors inspectable code over a narrow library API.
Outlets and controller helpers implicit internal Storyboard and same-target code can reach them. No framework boundary is being exported.
Types implicit internal Not visible outside the app module. App-local sample architecture.

Reference code

QuiltSimulator/QuiltView.swift:12 — persisted-key names are private, while the view exposes semantic mode and color properties to its controller.

private static let straightLineStitchUserDefaultsKey = "useStrightLineStitch"
private static let colorUserDefaultsKey = "threadColor"

var useStraightLineStitch: Bool {
    get { UserDefaults.standard.bool(forKey: QuiltView.straightLineStitchUserDefaultsKey) }
    set { UserDefaults.standard.set(newValue,
                                    forKey: QuiltView.straightLineStitchUserDefaultsKey) }
}

Logic ownership and placement

Logic Owner Why it belongs there
Stitch capture, line construction, and crosshair region QuiltView All depend on editor bounds and stitch state.
Button pointer styles ViewController Styles are configured with controller-created toolbar buttons.
Swatch lift preview ThreadCell Preview path depends on the reused cell’s swatch.
Color selection routing Picker protocol and ViewController Picker emits intent; screen applies it to editor/tool UI.
Patchwork construction PatchworkView / ColorBlockView Visual composition has no editor-state dependency.

Design patterns

Pattern Source evidence Purpose or tradeoff
Custom view state owner QuiltSimulator/QuiltView.swift:15 Keeps editor input, preferences, and drawing coherent but makes the view stateful.
Weak delegate QuiltSimulator/ThreadPickerViewController.swift:17 Sends semantic output without coupling picker to presenter.
Pointer style strategy QuiltSimulator/ViewController.swift:56 Uses closures for buttons and delegates for custom views/cells.
Composite view QuiltSimulator/PatchworkView.swift:14 Builds repeated blocks from nested stack views.

Naming conventions

  • Types name visual roles: QuiltView, PatchworkView, ColorBlockView, and ThreadCell.
  • Commands use domain verbs such as ripLastStitch, ripAllStitches, and threadPickerDidPickColor.
  • useStraightLineStitch and stitchColor expose user concepts rather than storage keys.
  • sharedInit signals the common initializer path for programmatic and storyboard creation.

Architecture takeaways

  • Put pointer-region calculations beside the view geometry they describe.
  • Use a weak, semantic delegate when a presented picker needs to return one result.
  • Let the screen controller coordinate tools while the custom editor owns its durable interaction state.
  • Compose decorative views separately when they do not participate in editing behavior.

Source map

Source file Relevant symbols
QuiltSimulator/ViewController.swift Tool and button pointer coordination
QuiltSimulator/QuiltView.swift Stitch state, persistence, rendering, crosshair pointer
QuiltSimulator/ThreadPickerViewController.swift Picker delegate and cell lift effect
QuiltSimulator/PatchworkView.swift Patchwork composition
QuiltSimulator/ColorBlockView.swift Reusable striped block