I have a button whose image has some additional clear space around it. The highlight when tapped regularly looks fine, and doesn't effect the clear space. However, when holding down to bring up the context menu, a light gray highlight is applied to the entire image area, which looks bad.
Is there a way to customize this highlight so the color is clear?
One possibility that I've tried but doesn't work:
.contentShape(.contextMenuPreview, Circle().size(width: 0, height: 0))
The problem here is that it causes the entire view to disappear during the context menu animation and visibility. I want to change the color of the highlight to clear, while maintaining the small resize animation and of course the visibility of the button.
CodePudding user response:
SwiftUI doesn't offer an option to remove the background but with its UIKit counterpart UIContextMenuInteraction
you can implement
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configuration: UIContextMenuConfiguration, highlightPreviewForItemWithIdentifier identifier: NSCopying) -> UITargetedPreview?
And implement your own preview. It can look something like below.
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configuration: UIContextMenuConfiguration, highlightPreviewForItemWithIdentifier identifier: NSCopying) -> UITargetedPreview? {
print(#function)
interaction.view?.backgroundColor = .clear
let previewTarget = UIPreviewTarget(container: interaction.view!, center: interaction.view!.center)
let previewParams = UIPreviewParameters()
previewParams.backgroundColor = .clear
return .init(view: interaction.view!, parameters: previewParams, target: previewTarget)
}
You will get something like this with that code.
Below you will find a full implementation
struct CustomContextMenuView: View {
var body: some View {
Image(systemName: "person")
.resizable()
.frame(width: 100, height: 100)
.contextMenu(actions: [
UIAction(title: "Add to Favorites", image: UIImage(systemName: "heart.fill"), handler: { a in
print("Add to Favorites action")
})
], willEnd: {
//Called when the menu is dismissed
print("willEnd/onDismiss")
}, willDisplay: {
//Called when the menu appears
print("willDisplay/onAppear")
})
}
}
extension View {
func contextMenu(actions: [UIAction], willEnd: (() -> Void)? = nil, willDisplay: (() -> Void)? = nil) -> some View {
modifier(ContextMenuViewModifier(actions: actions, willEnd: willEnd, willDisplay: willDisplay))
}
}
struct ContextMenuViewModifier: ViewModifier {
let actions: [UIAction]
let willEnd: (() -> Void)?
let willDisplay: (() -> Void)?
func body(content: Content) -> some View {
Interaction_UI(view: {content}, actions: actions, willEnd: willEnd, willDisplay: willDisplay)
.fixedSize()
}
}
struct Interaction_UI<Content2: View>: UIViewRepresentable{
typealias UIViewControllerType = UIView
@ViewBuilder var view: Content2
let actions: [UIAction]
let willEnd: (() -> Void)?
let willDisplay: (() -> Void)?
func makeCoordinator() -> Coordinator {
return Coordinator(parent: self)
}
func makeUIView(context: Context) -> some UIView {
let v = UIHostingController(rootView: view).view!
context.coordinator.contextMenu = UIContextMenuInteraction(delegate: context.coordinator)
v.addInteraction(context.coordinator.contextMenu!)
return v
}
func updateUIView(_ uiView: UIViewType, context: Context) {
}
class Coordinator: NSObject, UIContextMenuInteractionDelegate{
var contextMenu: UIContextMenuInteraction!
let parent: Interaction_UI
init(parent: Interaction_UI) {
self.parent = parent
}
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
UIContextMenuConfiguration(identifier: nil, previewProvider: nil, actionProvider: { [self]
suggestedActions in
return UIMenu(title: "", children: parent.actions)
})
}
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willDisplayMenuFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
print(#function)
parent.willDisplay?()
}
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willEndFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
print(#function)
parent.willEnd?()
}
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configuration: UIContextMenuConfiguration, highlightPreviewForItemWithIdentifier identifier: NSCopying) -> UITargetedPreview? {
print(#function)
let previewParams = UIPreviewParameters()
previewParams.backgroundColor = .clear
return UITargetedPreview(view: interaction.view!, parameters: previewParams)
}
}
}
struct CustomContextMenuView_Previews: PreviewProvider {
static var previews: some View {
CustomContextMenuView()
}
}