I'm trying to implement "Context Menu" in iOS13 in a table view (tried collection view as well, they have same issue):
I followed the instruction here , created a collection view with bunch of photos, and when 3d touch, I will get a preview with an action list, like this:
Peek and Menu View
Collection View
The delegate method in UITableView:
- (void)tableView:(UITableView *)tableView willCommitMenuWithAnimator:
(id<UIContextMenuInteractionCommitAnimating>)animator;
and in collectionView
- (void)collectionView:(UICollectionView *)collectionView willCommitMenuWithAnimator:
(id<UIContextMenuInteractionCommitAnimating>)animator;
both don't have "indexPath
";
So there is no way for me to pass in the current cell or index I'm peeking.
I want to implement this delegate method so when I tap on the preview image, it will navigate to the expected page with information that's bounded to the current cell.
What I want and tried is something like this:
func collectionView(_ collectionView: UICollectionView, willCommitMenuWithAnimator animator: UIContextMenuInteractionCommitAnimating) {
animator.addAnimations {
self.show(PreviewViewController(image: UIImage(named: "indexpathString")), sender: self)
}
}
and the indexpathString
is related to the current selected cell or collection so I can init the viewController based on it.
Please let me know if there is any other way to do this.
The context menu (right-hand mouse button or SHIFT+F10 ) allows you to display a context-sensitive menu. The context is defined by the position of the mouse pointer when the user requests the menu. A context menu allows the user to choose functions that are relevant to the current context.
You access contextual menus using a touch and hold gesture or 3D Touch if you have it. This makes it available on all devices running iOS 13 or later. The contextual menu presents a menu of actions and a preview of the affected item. You can create submenus though Apple recommends limiting them to one level.
In iOS and iPadOS, a context menu can display a preview of the current content near the list of commands. People can choose a command in the menu or — in some cases — they can tap the preview to open it or drag it to another area. Prefer a graphical preview that clarifies the target of a context menu's commands.
As of iOS 14 and SwiftUI 2 we can now add a pop out menu to any button in our app. We can implement this by using the new Menu keyword. Menus in some way are going to replace the current action sheets used in iOS apps. The problem with action sheets is they only show up at the bottom of our screen.
There's a property in the animator
called previewViewController
that is of type UIViewController?
. This is the same as the previewProvider
you set in tableView(_:contextMenuConfigurationForRowAt:point:)
, so if you set the previewProvider
as your destination view controller, you can do something like this:
Set your previewProvider
override func tableView(
_ tableView: UITableView,
contextMenuConfigurationForRowAt indexPath: IndexPath,
point: CGPoint
) -> UIContextMenuConfiguration? {
return UIContextMenuConfiguration(
identifier: nil,
previewProvider: { () -> UIViewController? in
let string = yourDataSource[indexPath.row]
return PreviewViewController(image: UIImage(named: string))
}
)
// actionProvider has a default value of nil, so you can omit it
}
Use the animator to show it
override func tableView(
_ tableView: UITableView,
willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration,
animator: UIContextMenuInteractionCommitAnimating
) {
guard let destinationViewController = animator.previewViewController else {
return
}
animator.addAnimations {
self.show(destinationViewController, sender: self)
}
}
The function contextMenuInteraction(_:willCommitWithAnimator:)
has been superseded by tableView(_:willPerformPreviewActionForMenuWith:animator:)
, which provides the UIContextMenuConfiguration
associated with the presented menu. At the point where your menu is presented, assign an identifier which will allow you to identify the row. The identifier should be any NSCopying
object - e.g. an NSIndexPath
:
override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
// Pass the indexPath as the identifier for the menu configuration
return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: nil) { _ in
// Empty menu for demonstration purposes
return UIMenu(title: "", children: [])
}
}
and then use that identifier in the willPerformPreviewActionForMenuWith:animator:
function:
override func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
guard let indexPath = configuration.identifier as? IndexPath else { return }
// Use your index path to get your destination VC in some way; arbitrary example shown...
let destinationVc = destinationViewController(for: indexPath)
// Then animate the appearance of the VC with the provided animator
animator.addAnimations {
self.show(destinationVc, sender: self)
}
}
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