WWDC Quick Look đź’“ By SwiftGGTeam
Squeeze the most out of Apple Pencil

Squeeze the most out of Apple Pencil

Watch original video

Highlight

iPadOS 18’s PencilKit tool picker finally supports custom tools—your stamps, erasers, and retouching tools can go directly into the system toolbar and automatically gain Apple Pencil Pro squeeze, barrel roll, and haptic feedback.

Core Content

Drawing app developers have long faced a choice: use PencilKit for a great toolbar experience but limited built-in tools (pen, eraser, lasso), or build your own canvas and toolbar for flexible tools but implement Apple Pencil Pro features like squeeze, hover preview, and barrel roll from scratch—a huge amount of work.

This year Apple offers a middle ground: PKToolPickerCustomItem. Register custom tools in the PencilKit toolbar and users can switch to your tools in the system toolbar. When a custom tool is selected, PencilKit’s canvas stops drawing automatically and your code takes over rendering. The toolbar’s color picker and width slider still work, and you can pass in your own UIViewController for extra controls. Barrel roll, squeeze, and other interactions come free with custom tools—zero extra code.

Apple Pencil Pro also brings three new interaction capabilities: the squeeze gesture (pinch the barrel to trigger), barrel roll angle (rotate the barrel to control stroke direction), and position-based haptic feedback (tactile response when aligning to guides and recognizing shapes). PencilKit built-in tools support these automatically; custom canvases can integrate via the new UIPencilInteraction, UICanvasFeedbackGenerator, and SwiftUI’s .onPencilSqueeze and .sensoryFeedback.

Detailed Content

Toolbar customization: toolItems initializer

Since iOS 13, PKToolPicker only had the default full tool set. iPadOS 18 adds a toolItems initializer to specify tool order and types in the toolbar (02:39). You can place multiple tools of the same type—for example a red pen and a blue pen—very useful for annotation apps.

Toolbar item types include: PKInkingToolItem (ink), PKEraserToolItem (eraser), PKLassoToolItem (lasso), PKRulerItem (ruler, tap to toggle), PKScribbleItem (handwriting to text), PKToolPickerCustomItem (custom tool), plus a trailing UIBarButtonItem for non-drawing actions (04:55).

Custom tools: PKToolPickerCustomItem

The core of custom tools is PKToolPickerCustomItem and its Configuration struct (06:10). Configuration defines supported properties—color, width, and opacity use PencilKit controls; extra properties are shown via your UIViewController. You provide a closure to render the tool icon; PencilKit calls it automatically when color, width, or opacity changes; call reloadImage() manually when custom properties change.

When a custom tool is selected, all associated PKCanvasView instances stop drawing automatically and your code handles touch events and rendering. PencilKit only manages tool selection UI.

Squeeze gesture

Apple Pencil Pro’s squeeze gesture lets users bring up the toolbar or contextual panel without lifting the pencil (09:20). A global system preference determines what squeeze triggers: show contextual palette, run a shortcut, or custom behavior. If the user chose a shortcut, your app won’t receive squeeze events.

Responding to squeeze in UIKit:

class MyViewController: UIViewController, UIPencilInteractionDelegate {

    func pencilInteraction(_ interaction: UIPencilInteraction,
               didReceiveSqueeze squeeze: UIPencilInteraction.Squeeze) {

        if UIPencilInteraction.preferredSqueezeAction == .showContextualPalette &&
           squeeze.phase == .ended {
           let anchorPoint = squeeze.hoverPose?.location ?? myDefaultLocation
           presentMyContextualPaletteAtPosition(anchorPoint)
        }
    }
}
  • UIPencilInteractionDelegate’s pencilInteraction(_:didReceiveSqueeze:) receives squeeze events (10:24)
  • preferredSqueezeAction checks the user’s system setting preference
  • squeeze.phase == .ended ensures triggering only when the gesture ends, avoiding duplicate responses
  • squeeze.hoverPose?.location gets the hover position at squeeze time for panel placement

Responding to squeeze in SwiftUI:

@Environment(\.preferredPencilSqueezeAction) var preferredAction
@State var contextualPalettePresented = false
@State var contextualPaletteAnchor = MyPaletteAnchor.default

var body: some View {
    MyView()
        .onPencilSqueeze { phase in
            if preferredAction == .showContextualPalette, case let .ended(value) = phase {
                if let anchorPoint = value.hoverPose?.anchor {
                    contextualPaletteAnchor = .point(anchorPoint)
                }
                contextualPalettePresented = true
            }
        }
}
  • @Environment(\.preferredPencilSqueezeAction) reads the system preference (10:46)
  • .onPencilSqueeze modifier receives squeeze events; phase distinguishes gesture stages
  • value.hoverPose?.anchor gets the hover anchor for panel placement

Haptic feedback: UICanvasFeedbackGenerator

Apple Pencil Pro haptic feedback adds two canvas-specific types: alignment and pathComplete (11:36). The feedback API now requires position information so the system can choose the best feedback method for the context.

Using in UIKit:

class MyViewController: UIViewController {
    @ViewLoading var feedbackGenerator: UICanvasFeedbackGenerator

    override func viewDidLoad() {
        super.viewDidLoad()
        feedbackGenerator = UICanvasFeedbackGenerator(view: view)
    }

    func dragAlignedToGuide(_ sender: MyDragGesture) {
        feedbackGenerator.alignmentOccurred(at: sender.location(in: view))
    }

    func snappedToShape(_ sender: MyDrawGesture) {
        feedbackGenerator.pathCompleted(at: sender.location(in: view))
    }
}
  • UICanvasFeedbackGenerator(view:) takes the canvas view at initialization (11:50)
  • alignmentOccurred(at:) triggers haptic feedback when a line aligns to a guide
  • pathCompleted(at:) triggers feedback when shape recognition completes and the stroke snaps to a regular shape

Using in SwiftUI:

@State var dragAlignedToGuide = 0
@State var snappedToShape = 0

var body: some View {
    MyView()
        .sensoryFeedback(.alignment, trigger: dragAlignedToGuide)
        .sensoryFeedback(.pathComplete, trigger: snappedToShape)
}
  • .sensoryFeedback(.alignment, trigger:) triggers alignment feedback when the trigger value changes (12:29)
  • .sensoryFeedback(.pathComplete, trigger:) triggers path-complete feedback when the trigger value changes
  • Attach modifiers to the most specific subview for more precise feedback

Barrel roll angle

Apple Pencil Pro barrel roll lets rotating the barrel control stroke angle (08:04). PencilKit marker and fountain pen support this built in. Custom canvases can read angle via UITouch.rollAngle and UIHoverGestureRecognizer.rollAngle. Apple recommends adding rollAngle and azimuth angle together so azimuth serves as fallback on devices without roll support (13:32). Note rollAngle starts as an estimate and is refined via Bluetooth updates—get the final precise value in touchesEstimatedPropertiesUpdated.

Core Takeaways

  • What to do: Register custom drawing tools in PKToolPickerCustomItem. Why it’s worth it: Users switch to your tools in the system toolbar; squeeze and barrel roll work automatically without implementing interaction logic yourself. How to start: Create PKToolPickerCustomItem.Configuration, define color/width properties, provide an icon rendering closure, then build the toolbar with the toolItems initializer.

  • What to do: Integrate squeeze gestures in custom canvases to pop up contextual tool panels. Why it’s worth it: Users switch tools or bring up options without lifting the pencil—the drawing flow isn’t interrupted. How to start: In UIKit, implement UIPencilInteractionDelegate’s pencilInteraction(_:didReceiveSqueeze:); in SwiftUI, use the .onPencilSqueeze modifier. Remember to check preferredSqueezeAction to respect system settings.

  • What to do: Trigger haptic feedback during shape recognition and alignment snapping. Why it’s worth it: Feedback lets users feel successful snapping before visual confirmation—a more natural feel. How to start: In UIKit, use UICanvasFeedbackGenerator and call alignmentOccurred(at:) and pathCompleted(at:); in SwiftUI, use .sensoryFeedback(.alignment, trigger:) and .sensoryFeedback(.pathComplete, trigger:).

  • What to do: Read barrel roll angle in custom brush tools to control stroke rotation. Why it’s worth it: Simulates real brush rotation for a digital drawing feel closer to paper and pen. How to start: Read angle from UITouch.rollAngle, add azimuthAngle for total rotation, and update the final precise value in touchesEstimatedPropertiesUpdated.

Comments

GitHub Issues · utterances