I created my custom context menu using UIView. Im trying to change the corner radius when the context menu is active, but I have no idea how. I tried masks, layer whatsoever, but nothing worked. The provided current code is not rounding the corners. When I tried to set the background color to clear, then in between the fuzzy color and the content appeared a color that is the background color. Any idea?
Here is the undesired behavior (you can see the distinct corner rounding):
import SwiftUI
struct CustomContextMenu<Content: View>: View {
var content: Content
var menu: UIMenu?
var previewBackgroundColor: UIColor
init(
@ViewBuilder content: @escaping ()->Content,
actions: @escaping ()-> UIMenu?,
previewBackgroundColor: UIColor
) {
self.content = content()
self.menu = actions()
self.previewBackgroundColor = previewBackgroundColor
}
var body: some View {
content
.hidden()
.overlay(
ContextMenuHelper(content: content, actions: menu, previewBackgroundColor: previewBackgroundColor)
)
}
}
struct ContextMenuHelper<Content: View>: UIViewRepresentable
{
var content: Content
var actions: UIMenu?
var previewBackgroundColor: UIColor
init(content: Content, actions: UIMenu?, previewBackgroundColor: UIColor) {
self.content = content
self.actions = actions
self.previewBackgroundColor = previewBackgroundColor
}
func makeCoordinator() -> Coordinator {
return Coordinator(parent: self )
}
func makeUIView(context: Context) -> UIView {
// uiview
let view = UIView()
view.backgroundColor = UIColor.clear
view.layer.cornerRadius = 20
// host view
let hostView = UIHostingController(rootView: content)
hostView.view.translatesAutoresizingMaskIntoConstraints = false
hostView.view.backgroundColor = .clear
view.addSubview(hostView.view) // add subview
view.addConstraints( // add constraints
[
hostView.view.topAnchor.constraint(equalTo: view.topAnchor),
hostView.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
hostView.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
hostView.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
hostView.view.widthAnchor.constraint(equalTo: view.widthAnchor),
hostView.view.heightAnchor.constraint(equalTo: view.heightAnchor)
]
)
if self.actions != nil { // if any menu item has been loaded
// interaction
let interaction = UIContextMenuInteraction(delegate: context.coordinator)
view.addInteraction(interaction)
}
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
}
class Coordinator: NSObject, UIContextMenuInteractionDelegate {
var parent: ContextMenuHelper
init(parent: ContextMenuHelper) {
self.parent = parent
}
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
interaction.view?.layer.cornerRadius = 30
guard let viewFrame = interaction.view?.frame else { return nil } // obtain child view size
return UIContextMenuConfiguration(identifier: nil) {
let previewController = UIHostingController(rootView: self.parent.content)
previewController.view.backgroundColor = self.parent.previewBackgroundColor
previewController.preferredContentSize = CGSize(width: viewFrame.width, height: viewFrame.height)
return previewController
} actionProvider: { items in
return self.parent.actions
}
}
}
}
EDIT:
When Im wrapping the view into another view it changes the inner view, not the outer view:
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
interaction.view?.layer.cornerRadius = 30
guard let viewFrame = interaction.view?.frame else { return nil } // obtain child view size
return UIContextMenuConfiguration(identifier: nil) {
let previewView = self.parent.content
.background(Color(self.parent.previewBackgroundColor).edgesIgnoringSafeArea(.all))
.cornerRadius(30)
let previewController = UIHostingController(rootView: previewView)
previewController.preferredContentSize = CGSize(width: viewFrame.width, height: viewFrame.height)
return previewController
} actionProvider: { items in
return self.parent.actions
}
}
So I resolved the issue by changing contextMenuInteraction method contents and by rounding view which wraps host view.
struct ContextMenuHelper<Content: View>: UIViewRepresentable
{
var content: Content
var actions: UIMenu?
var previewBackgroundColor: UIColor
init(content: Content, actions: UIMenu?, previewBackgroundColor: UIColor) {
self.content = content
self.actions = actions
self.previewBackgroundColor = previewBackgroundColor
}
func makeCoordinator() -> Coordinator {
return Coordinator(parent: self )
}
func makeUIView(context: Context) -> UIView {
// uiview
let view = UIView()
view.layer.cornerRadius = 20
// host view
let hostView = UIHostingController(rootView: content)
hostView.view.translatesAutoresizingMaskIntoConstraints = false
hostView.view.backgroundColor = .clear
view.addSubview(hostView.view) // add subview
view.addConstraints( // add constraints
[
hostView.view.topAnchor.constraint(equalTo: view.topAnchor),
hostView.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
hostView.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
hostView.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
hostView.view.widthAnchor.constraint(equalTo: view.widthAnchor),
hostView.view.heightAnchor.constraint(equalTo: view.heightAnchor)
]
)
if self.actions != nil { // if any menu item has been loaded
// interaction
let interaction = UIContextMenuInteraction(delegate: context.coordinator)
view.addInteraction(interaction)
}
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
}
class Coordinator: NSObject, UIContextMenuInteractionDelegate {
var parent: ContextMenuHelper
init(parent: ContextMenuHelper) {
self.parent = parent
}
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
let configuration = UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { suggestedActions in
return self.parent.actions
}
return configuration
}
}
}