swift custom context menu previewprovider can not click any view inside(using tapgesture)
i changed to present new VC with hero animation, so bored, but it is my best choice for now
Remove BackgroundView from UITargetedPreview in Swift
There is a tricky method to hide the shadow and to do that you should find a view with _UIPlatterSoftShadowView
class name in the view hierarchy and then hide it.
func viewByClassName(view: UIView, className: String) -> UIView? {
let name = NSStringFromClass(type(of: view))
if name == className {
return view
}
else {
for subview in view.subviews {
if let view = viewByClassName(view: subview, className: className) {
return view
}
}
}
return nil
}
override func tableView(_ tableView: UITableView, willDisplayContextMenu configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
DispatchQueue.main.async {
if let window = UIApplication.shared.delegate?.window! {
if let view = self.viewByClassName(view: window, className: "_UIPlatterSoftShadowView") {
view.isHidden = true
}
}
}
}
NOTE: It's not documented internal class and can be changed anytime further but it works now on both ios 13/14.
Present VC modally above other VC - both touchable
present
function will make the second view controller cover the whole page and you will no longer have access from screen to the view controller below the top one.
To accomplish your goal, you need to put a UITableView
on a view, then keep showing or hiding the view. If you really want another view controller, you can create a containing view and have a embed segue to another view controller
, like this
How to disable long-press animation of context menu for CollectionView iOS Swift?
Pass return nil
insted of return UIContextMenuConfiguration(identifier: nil, previewProvider: nil)
func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
switch collectionView {
case favoriteCollectionView:
return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) {...}
case allCollectionView:
return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) {...}
default:
return nil //<-- HERE
}
}
How to create Whats App preview animation?
That is UIContextMenuConfiguration
which supports iOS13
onwards.
You can see a preview and menu when you long press on tableViewCell. You can display any viewController
or any customView
as menu's preview component.
I have displayed a viewController as preview.
// MainViewController is first ViewController and DetailsViewController is second ViewController
class MainViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var castNameArr = ["Professor", "Berlin", "Nairobi", "Tokiyo", "Rio", "Denver", "Mosco", "Requel", "Helsinki", "Arturo"]
var selectedCast = ""
var detailVc = DetailsViewController()
override func viewDidLoad() {
super.viewDidLoad()
title = "Money Heist Casts"
detailVc = self.storyboard?.instantiateViewController(withIdentifier: "DetailsViewController") as! DetailsViewController
}
}
Using contextMenuConfigurationForRowAt
method of tableView we can create menu and preview.
func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
selectedCast = castNameArr[indexPath.row]
let identifier = "\(indexPath.row)" as NSString
return UIContextMenuConfiguration(
identifier: identifier,
previewProvider: makePreview) { _ in
let optionOne = UIAction(
title: "Option 1",
image: UIImage(systemName: "arrowshape.turn.up.right")){ _ in
//This will get called when you tap on Option 1
}
let optionTwo = UIAction(
title: "Option 2",
image: UIImage(systemName: "book.fill")){ _ in
//This will get called when you tap on Option 2
}
return UIMenu(title: "", image: nil, children: [optionOne, optionTwo])
}
}
// This will return a viewController which we want to display as preview
func makePreview() -> UIViewController {
detailVc.castNameStr = selectedCast
return detailVc
}
Using willPerformPreviewActionForMenuWith
method will get called when the interaction is about to "commit" in response to the user tapping the preview.
func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
DispatchQueue.main.async {
print(configuration.identifier as? String)
self.navigationController?.pushViewController(self.detailVc, animated: true)
}
}
This is DetailsViewController which is shown as preview
class DetailsViewController: UIViewController {
@IBOutlet weak var titleLbl: UILabel!
@IBOutlet weak var castImgView: UIImageView!
var castNameStr = ""
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
titleLbl.text = "This is \(castNameStr)"
castImgView.image = UIImage(named: castNameStr)
}
Download demo from Here
For more details you can check here
https://www.raywenderlich.com/6328155-context-menus-tutorial-for-ios-getting-started
https://medium.com/better-programming/creating-a-context-menu-and-sf-symbol-in-swift-e45459f5f704
SwiftUI detect when contextMenu is open
A possible approach is to use simultaneous gesture for this purpose, like
Text("Demo Menu")
.contextMenu(menuItems: {
Button("Button") {}
})
.simultaneousGesture(LongPressGesture(minimumDuration: 0.5).onEnded { _ in
print("Opened")
})
Tested with Xcode 13.2 / iOS 15.2
iOS 14 Context Menu from UIView (Not from UIButton or UIBarButtonItem)
After some experimentation I was able to remove the dimming blur, like this. You will need a utility method:
extension UIView {
func subviews<T:UIView>(ofType WhatType:T.Type,
recursing:Bool = true) -> [T] {
var result = self.subviews.compactMap {$0 as? T}
guard recursing else { return result }
for sub in self.subviews {
result.append(contentsOf: sub.subviews(ofType:WhatType))
}
return result
}
}
Now we use a context menu interaction delegate method to find the UIVisualEffectView that is responsible for the blurring and eliminate it:
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willDisplayMenuFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
DispatchQueue.main.async {
let v = self.view.window!.subviews(ofType:UIVisualEffectView.self)
if let v = v.first {
v.alpha = 0
}
}
}
Typical result:
Unfortunately there is now zero shadow at all behind the menu, but it's better than the big blur.
And of course it’s still a long press gesture. I doubt anything can be done about that! If this were a normal UILongPressGestureRecognizer you could probably locate it and shorten its minimumPressDuration
, but it isn't; you have to subject yourself to the UIContextMenuInteraction rules of the road.
However, having said all that, I can think of a much better way to do this, if possible: make this UIView be a UIControl! Now it behaves like a UIControl. So for example:
class MyControl : UIControl {
override func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
let config = UIContextMenuConfiguration(identifier: nil, previewProvider: nil, actionProvider: { _ in
let act = UIAction(title: "Red") { action in }
let act2 = UIAction(title: "Green") { action in }
let act3 = UIAction(title: "Blue") { action in }
let men = UIMenu(children: [act, act2, act3])
return men
})
return config
}
}
And:
let v = MyControl()
v.isContextMenuInteractionEnabled = true
v.showsMenuAsPrimaryAction = true
v.frame = CGRect(x: 100, y: 100, width: 200, height: 100)
v.backgroundColor = .red
self.view.addSubview(v)
And the result is that a simple tap summons the menu, which looks like this:
So if you can get away with that approach, I think it's much nicer.
Related Topics
Xcode 7.3/Swift 2: "No Method Declared with Objective-C Selector" Warning
How to Create an Instance of a Class from a String in Swift
Swift: Print Decimal Precision of Division
In Xcode 6.1. 'Uiimage' Does Not Have a Member Named 'Size' Error
Extension of Dictionary Where <String, Anyobject>
Determine If Any.Type Is Optional
How to Check If a Property Value Exists in Array of Objects in Swift
How to Determine the Type of a Variable in Swift
Multiple Type Constraints in Swift
How to Create Custom Notifications in Swift 3
How to Create a Fixed-Size Array of Objects
Override Func Error in Swift 2
Building Pure Swift Cocoa Touch Framework
How to Scale Text to Fit Parent View with Swiftui
How to Set the Blurradius of Uiblureffectstyle.Light
Load Image from iOS 8 Framework
Swift: What Does Backslash Dot "\." Mean
Determining If Swift Dictionary Contains Key and Obtaining Any of Its Values