Update for iOS 13.4 (March, 2020):
This also happens with UIPointerInteraction
when hovering over the links.
I have a view that displays rich text and shows the iOS 13 context menu when the user long presses on a link. I want to be able to highlight just the link rather than the whole view when the user begins to long press.
To do this I provide a UITargetedPreview
object that contains UIPreviewParameters
with the CGRect
s of each line to be highlighted to the UIContextMenuInteractionDelegate
of the view. This correctly highlights the link, but has the unwanted side effect of also hiding the rest of the view.
This image demonstrates the problem:
Notice that, while the link is highlighted correctly, the remainder of the view flashes in and out as the link is long pressed and then released.
Compare this to the behaviour in Apple's own Notes.app:
Notice that the rest of the view does not disappear when a link is long pressed. This also works as expected in Apple's other apps, too (e.g. Safari).
I provide UITargetedPreview
s to the interaction delegate in the following way:
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, previewForHighlightingMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
guard let range = configuration.identifier as? NSRange else { return nil }
let lineRects: [NSValue] = // calculate appropriate rects for the range of text
let parameters = UIPreviewParameters(textLineRects: lineRects)
return UITargetedPreview(view: /* the rich text view */, parameters: parameters)
}
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, previewForDismissingMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
guard let range = configuration.identifier as? NSRange else { return nil }
let lineRects: [NSValue] = // calculate appropriate rects for the range of text
let parameters = UIPreviewParameters(textLineRects: lineRects)
return UITargetedPreview(view: /* the rich text view */, parameters: parameters)
}
I can't find anything in what documentation there is for UITargetedPreview
and UIPreviewParameters
, so does anyone know how this can be done?
OK, I finally found out how to do it thanks to an implementation in WebKit. What I was doing wrong was not providing the targeted preview a UIPreviewTarget
.
To highlight only a portion of a view you need to:
Provide UIPreviewParameters stating the portion of the view to show in the preview, and set the background colour to the same as the view you want to preview
Provide a UIPreviewTarget that establishes:
Create a snapshot of the portion of the view you want to show and designate it as view of the UITargetedPreview.
In code this looks like:
let view: UIView = // the view with the context menu interaction
let highlightedRect: CGRect = // the rect of the highlighted portion to show
// 1
let previewParameters = UIPreviewParameters()
previewParameters.visiblePath = // path of highlighted portion of view to show, remember you can use UIPreviewParameters.init(textLineRects:) for text
previewParameters.backgroundColor = view.backgroundColor
// 2: Notice that we're passing the whole view in here
let previewTarget = UIPreviewTarget(container: view, center: CGPoint(x: highlightedRect.midX, y: highlightedRect.midY))
// 3: Notice that we're passing the snapshot view in here - not the whole view
let snapshot = view.resizableSnapshotView(from: highlightedRect, afterScreenUpdates: true, withCapInsets: .zero)!
return UITargetedPreview(view: snapshot, parameters: previewParameters, target: previewTarget)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With