Highlight
The design concept of iPadOS’s pointer system is “indirect touch” - the pointer is an extension of the finger rather than the mouse pointer of the traditional desktop system.Many system components and some standard controls already have built-in pointer support; controls such as UIButton can be enabled and customized through the API, and custom UI needs to be adapted with UIPointerInteraction.
Core Content
iPadOS 13.4 adds universal pointing device support to iPad.Problems arise: the iPad’s interface has long been designed around finger touch, and the clickable areas, feedback methods, and drag gestures of many controls assume that users touch the screen directly.Customizing buttons, canvases, text areas, and toolbars can easily become stiff if you just bring in the desktop mouse model.
Apple’s recommendation is to work your way down from the high-level API.UIBarButtonItem、UISegmentedControl、UIMenuController、UIScrollView、UITextView、UIDragInteractionandUIContextMenuInteractionSystem level support has been obtained.Using these ready-made behaviors first, and then processing your own custom views can keep the App and the system consistent (03:16).
Where developers really need to get their hands dirty are with views that look like controls but whose semantics UIKit cannot automatically understand.Session uses a Quilt Simulator to demonstrate: the ruler button in the lower right corner needs a more appropriate highlight range, the spool button in the upper right corner needs a lift effect, and the central sewing area needs a crosshair and a pointer area that snaps to the grid.
The main thread of this sharing is very clear: let the system controls get the default pointer feedback first, and then useUIButtonconvenience API spinner, and finally useUIPointerInteraction、UIPointerRegion、UIPointerStyleand delegate animations to handle custom UI.
Detailed Content
1. First enable the built-in pointer effect of UIButton
(06:04)UIButtonA two-stage interface is provided.The first step is to openisPointerInteractionEnabled, the system will give a default effect based on the button’s appearance, size and content.For the second steppointerStyleProviderReceive the effect and shape proposed by the system, and then replace part of them.
// Enable the button's built-in pointer interaction.
myButton.isPointerInteractionEnabled = true
// Customize the default interaction effect.
myButton.pointerStyleProvider = { button, proposedEffect, proposedShape -> UIPointerStyle? in
// In this example, we'll switch to using the .lift effect by creating a new
// UIPointerEffect with the .lift type using the proposedEffect's preview.
return UIPointerStyle(effect: .lift(proposedEffect.preview), shape: proposedShape)
}
Key points:
isPointerInteractionEnabled = trueLet the button enter the system pointer system.pointerStyleProviderCalled when suspended, suitable for button-level customization.proposedEffectandproposedShapeJudging from the system, reusing them first can reduce unnecessary visual bias..lift(proposedEffect.preview)Suitable for buttons that have their own shape, such as the spool button in the demo.- return
nilThe system continues to use the default style; returnUIPointerStyleWhen using custom styles.
2. Use content effect to adjust the floating range
(07:05) Pointer styles are divided into content effect and shape customization.The content effect transforms the pointer into a view and applies visual processing to the view.The problem with the ruler button in Session is that the highlighted area is too tight. The solution is to add a little padding to the pointer shape.
// Create a UIPointerStyle that applies the .highlight effect.
// Outset the view's frame so the pointer shape has some generous padding around the view's contents.
// Note that this frame must be in the provided UITargetedPreview's container's coordinate space.
// In the majority of cases (where the preview doesn't have a custom container), this is just the view's superview.
let rect = myView.frame.insetBy(dx: -8.0, dy: -4.0)
let preview = UITargetedPreview(view: myView)
return UIPointerStyle(effect: .highlight(preview), shape: .roundedRect(rect))
Key points:
myView.frame.insetBy(dx: -8.0, dy: -4.0)Use negative values to enlarge the rectangle so that the pointer snaps to the button earlier.UITargetedPreview(view:)Tells the system which views participate in hover visual effects..highlight(preview)Corresponds to the common highlighting effects of toolbars and buttons..roundedRect(rect)Determines the deformed outer contour of the pointer.- The comment emphasizes that the coordinate space must be aligned with the container of the preview, otherwise the highlight position will be offset.
3. Use shape customization to express context
(08:02) Shape customization only changes the pointer shape and movement constraints.Text areas are a classic example: the pointer becomes a vertical beam and is constrained to the vertical axis, moving as if it were attached to the line of text.
// Create a UIPointerStyle that changes the pointer into a vertical beam.
let beamLength = myFont.lineHeight
return UIPointerStyle(shape: .verticalBeam(length: beamLength), constrainedAxes: .vertical)
Key points:
myFont.lineHeightMake the beam height consistent with the current text layout..verticalBeam(length:)Expressive text inserts or pinpoints scenes.constrainedAxes: .verticalMakes the pointer appear constrained along a specified axis.- This mode also appears in the straight-line guidance mode of Quilt Simulator: the pointer adsorbs on the horizontal grid lines to help the user sew straight lines (17:45).
4. Use UIPointerInteraction to manage custom areas
(13:09) The custom canvas does not have semantics such as UIButton, and the Session option is added directlyUIPointerInteraction.By default, interaction will cover the entire view; if you want to switch styles within a sub-area, implementUIPointerInteractionDelegateregion request and style request, put the differenceUIPointerRegionmapped to differentUIPointerStyle。
This mechanism can also expand the effective area.Session mentioned at 19:20 that a larger pointer region can be provided to enhance the “magnetism”, but the region must fall within the hit-testable area of the interaction view.If the region exceeds the view, you need to override hit-test at the same time so that the expanded area can hit the view (19:20).
5. Add additional animation to pointer entry and exit
(21:31) The last layer of polish is to adjust other UIs simultaneously with the pointer animation.UIPointerInteractionDelegateofwillEnterandwillExitAnimator will be provided, and developers can add their own animations to the same animation timeline.For SessionUISegmentedControlThe hidden separator example explains this technique.
func pointerInteraction(_ interaction: UIPointerInteraction,
willEnter region: UIPointerRegion,
animator: UIPointerInteractionAnimating) {
// Fade out separator when entering region.
animator.addAnimations {
self.separatorView.alpha = 0.0
}
}
Key points:
willEnter regionTriggered when the pointer enters a region.animator.addAnimationsThe custom animation will be connected to the system pointer animation.separatorView.alpha = 0.0The adjustment is made to the surrounding chrome to reduce visual noise in the hover state.
func pointerInteraction(_ interaction: UIPointerInteraction,
willExit region: UIPointerRegion,
animator: UIPointerInteractionAnimating) {
// Fade separator back in when exiting region.
animator.addAnimations {
self.separatorView.alpha = 1.0
}
}
Key points:
willExit regionUsed to restore the interface state before entering.- Using the same animator can avoid inconsistent rhythms between separator and pointer effects.
- This type of animation is suitable for hiding dividers, secondary chrome or temporary prompts, with the goal of making the hover state cleaner.
Core Takeaways
-
Complete Custom Toolbar Buttons: What to do: Check all passed
customViewPut the button into bar.Why it’s worth doing: The system can only handle standards automaticallyUIBarButtonItem, custom view needs to declare pointer behavior by itself.How to start: Open firstisPointerInteractionEnabled, then usepointerStyleProviderReuse the effects and shapes proposed by the system. -
Add precise pointers to the canvas interface: What to do: Use special pointer shapes in areas such as drawing, editing, typesetting, map annotation, etc.Why it’s worth it: Session’s Quilt Simulator makes click placement clearer with crosshairs and axial constraints.How to start: Add to canvas
UIPointerInteraction, returned in delegate for different regionsUIPointerStyle(shape:constrainedAxes:)。 -
Use pointer region to enlarge the snapping range of key controls: What: Makes small buttons, narrow handles, and dense list items easier to snap when the pointer is close.Why it’s worth doing: Session clearly mentioned that extending the region can enhance magnetism and reduce the cost of precise movement.How to start: Return wider than visual bounds
UIPointerRegion, and confirm that the area is within the hit-testable view range. -
Link the hover state with the interface chrome: What to do: Hide the divider, weaken the background, or display temporary auxiliary information when the pointer enters a certain area.Why it’s worth doing:
willEnterandwillExitThe animator can synchronize these changes with the system pointer animation.How to start: Implement two delegate methods and putseparatorView.alpha, auxiliary label transparency or lightweight highlighting addedanimator.addAnimations。
Related Sessions
- Design for the iPadOS pointer — Explains the iPadOS pointer’s adaptive precision, deformation, gestures, and keyboard modifiers from a design perspective.
- Handle trackpad and mouse input — Explain pointer movement, pointer lock, scroll input, trackpad gestures and mouse button events.
- Build for iPad — Shows how to transform a more complete iPad app with multi-column layouts, lists, and low-modal navigation.
- Designed for iPad — Starting from iPad design patterns, covering sidebar, drag and drop, keyboard, trackpad and adaptive layout.
Comments
GitHub Issues · utterances