One Touch at a Time
Ariandel's canvas acts on one touch at a time. The view keeps a single activeTouch, documented as "the one touch acting on the canvas (a stroke, a selection, a drag, a tap)", and touchesBegan only gives the canvas to a new touch while that slot is empty. The rule is sound. Two simultaneous strokes would fight over one Painter, and a second finger is supposed to turn into navigation, not paint. But a slot that is emptied only by an event has a weak spot: it trusts that the event arrives.
The Dead Pencil
On 2026-10-03 Dad reported it from his iPad: "sometimes, after touching with his hand, the Pencil stops drawing; tapping other tools a few times and coming back to the brush brings it back." The commit that answered him (de3bef7, 12:05 the same day) names the mechanism. If a touch's end never reached the view, held back or lost, activeTouch stayed set and every Pencil stroke after it was refused. The way out Dad had found by hand was real too: opening a panel cancels every touch in the window, the cancel clears the slot, and the Pencil works again. That is why it looked like the tools "fixed" it.
Notice what the bug did not need: a crash, an exception, a race between threads. It needed one missing callback and one piece of state that waited for it.
reclaimCanvas: Stop Waiting for the End
The fix turns the question around. Instead of waiting for the old touch to say goodbye, every new landing checks whether the old touch is really still there. reclaimCanvas(for:) (first code block) is the first thing touchesBegan does to the canvas's state, and it has three rules:
- If the held touch's phase is already
.endedor.cancelled, its end simply never reached the view. Finish it as if it had lifted. - If a Pencil lands while an earlier Pencil touch is held, the earlier one must be over, because there is only one Pencil. Finish it the same way.
- If a Pencil lands while a finger holds the canvas, the Pencil takes it. The finger's work is abandoned, and for a stroke that means every tile it touched is put back.
"Finish as lifted" goes through the same finishActiveTouch a real lift uses, with no event: the held touch is fed once more as it stands and the stroke ends normally. The compiled sketch in the third code block models the slot before and after, for three shapes the lost end could have taken. Without the rule, three Pencil strokes are refused in every case. With it, all three draw. The Drawing Pad received the first rule as well: a new touch finishes a held one whose phase is over. The house rules keep the lesson in one line: "The canvas never waits on a touch's end to let the Pencil in."
Everything Else the Pencil Owns
- Double-tap and squeeze. A
UIPencilInteractionon the canvas forwards both. Each one is configured separately to one of eight actions: As the iPad Says (the system's preferred tap or squeeze action), Brush and Eraser, Previous Tool, Colors, QuickMenu, Eyedropper, Undo, Nothing. A squeeze acts when it is released, and the eyedropper and QuickMenu act at the point where the Pencil is hovering. - Hover. A
UIHoverGestureRecognizertracks the hovering Pencil. In paint mode it draws the brush's outline, if the brush cursor is on, and Liquify always shows its size. With hover gestures on, which is the default, a two-finger pinch sizes the brush and a one-finger slide sets its opacity while the Pencil hovers. Pan and rotation are refused for that time, so the canvas holds still. - Palm Support. Off until turned on. Standard treats a touch whose
majorRadiusis 42 points or more as a resting hand, and Fine lowers that to 30, at the cost of a broad fingertip now and then (second code block). Once a touch is judged a palm it stays one until it lifts. It never acts, never reaches a navigation gesture, and does not count as a finger. A finger that spreads into a palm mid-stroke has its stroke dropped.
What the Tests Cannot Say
Every behavior in this lesson begins with a real Apple Pencil, and the test gate runs on an iPad simulator, which has none. The de3bef7 commit says it in its last lines: "The lost end itself was not reproduced; the simulator has no Pencil. Dad's iPad is the witness." The coop history rows for those rounds keep the same honesty. For squeeze and double-tap, the gestures "were tested through the model". Pencil hover, so the hover gestures too, was tested only through the model. A real palm, and the lost touch end on Dad's iPad, were not exercised at all. What the suites do prove is narrower than it sounds. Every Pencil gesture action and the palm thresholds have unit tests, and the commit's existing painting, selection, transform, fill and taper UI tests still passed. No test drops a touch's end on purpose; the sketch above is the closest thing to one. Whether the iPad ever loses an end again, and whether 42 points matches a real hand, are hardware facts. Track 8 turns that boundary into a practice.