UIMenuController with a custom item not working with UICollectionview

I added a custom menu controller with a long press on UICollectionViewCell

[self becomeFirstResponder]; UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:@"Custom Action" action:@selector(customAction:)]; [[UIMenuController sharedMenuController] setMenuItems:[NSArray arrayWithObject:menuItem]]; [[UIMenuController sharedMenuController] setTargetRect: self.frame inView:self.superview]; [[UIMenuController sharedMenuController] setMenuVisible:YES animated: YES]; 

canBecomeFirstResponder Also called

 - (BOOL)canBecomeFirstResponder { // NOTE: This menu item will not show if this is not YES! return YES; } 

// This method is not called

 - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { NSLog(@"canPerformAction"); // The selector(s) should match your UIMenuItem selector if (action == @selector(customAction:)) { return YES; } return NO; } 

I also implemented these methods

 - (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { if([NSStringFromSelector(action) isEqualToString:@"customAction:"]){ NSLog(@"indexpath : %@",indexPath); UIAlertView *alertview = [[UIAlertView alloc] initWithTitle:@"warning.." message:@"Do you really want to delete this photo?" delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil]; [alertview show]; return YES; } return YES; } - (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath { return YES; } - (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { NSLog(@"performAction"); } 

Although it only shows the cut, copy and paste menu

+6
source share
5 answers

I just spent two days trying to figure out the โ€œrightโ€ way to do this, and bark the wrong tree with some suggestions that are around.

This article shows you the correct way to do this. I hope that by posting it here, someone will be saved for several hours.

http://dev.glide.me/2013/05/custom-item-in-uimenucontroller-of.html

+5
source

Perhaps a little late, but I may have found a better solution for those who are still looking for this:

In the viewDidLoad of your UICollectionViewController add your element:

 UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:@"Title" action:@selector(action:)]; [[UIMenuController sharedMenuController] setMenuItems:[NSArray arrayWithObject:menuItem]]; 

Add the following delegate methods:

 //This method is called instead of canPerformAction for each action (copy, cut and paste too) - (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { if (action == @selector(action:)) { return YES; } return NO; } //Yes for showing menu in general - (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath { return YES; } 

Subclass of UICollectionViewCell, if you have not already done so. Add the method that you specified for your element:

 - (void)action:(UIMenuController*)menuController { } 

Thus, you do not need any startFirstResponder method or other methods. You have all the actions in one place, and you can easily handle different cells if you call a common method with the cell itself as a parameter.

Edit: somehow uicollectionview needs this method to exist (this method is not called for your custom action, I think uicollectionview just checks for availability)

 - (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { } 
+5
source

You need to call the delegate functions from a custom UICollectionViewCell

Here is my working example code for Swift3

CollectionViewController

 override func viewDidLoad() { super.viewDidLoad() let editMenuItem = UIMenuItem(title: "Edit", action: NSSelectorFromString("editCollection")) let deleteMenuItem = UIMenuItem(title: "Delete", action: NSSelectorFromString("deleteCollection")) UIMenuController.shared.menuItems = [editMenuItem, deleteMenuItem] } override func collectionView(_ collectionView: UICollectionView, shouldShowMenuForItemAt indexPath: IndexPath) -> Bool { return true } override func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) -> Bool { return action == NSSelectorFromString("editCollection") || action == NSSelectorFromString("deleteCollection") } override func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) { print("action:\(action.description)") //Custom actions here.. } 

Add the following functions to your custom UICollectionViewCell

 override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { return action == NSSelectorFromString("editCollection") || action == NSSelectorFromString("deleteCollection") } 

Calling a delegate function from a cell (should be in your custom UICollectionViewCell)

 func editCollection() { let collectionView = self.superview as! UICollectionView let d:UICollectionViewDelegate = collectionView.delegate! d.collectionView!(collectionView, performAction: NSSelectorFromString("editCollection"), forItemAt: collectionView.indexPath(for: self)!, withSender: self) } func deleteCollection() { let collectionView = self.superview as! UICollectionView let d:UICollectionViewDelegate = collectionView.delegate! d.collectionView!(collectionView, performAction: NSSelectorFromString("deleteCollection"), forItemAt: collectionView.indexPath(for: self)!, withSender: self) } 
+2
source

Swift 3 solution:

Just follow all the steps inside the UICollectionView class and assign this class to the UICollectionView object.

 import UIKit class MyAppCollectionView: UICollectionView { required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) addLongPressGesture() } func addLongPressGesture() { let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(MyAppCollectionView.longPressed(_:))) longPressGesture.minimumPressDuration = 0.5 self.addGestureRecognizer(longPressGesture) } func longPressed(_ gesture: UILongPressGestureRecognizer) { let point = gesture.location(in: self) let indexPath = self.indexPathForItem(at: point) if indexPath != nil { MyAppViewController.cellIndex = indexPath!.row let editMenu = UIMenuController.shared becomeFirstResponder() let custom1Item = UIMenuItem(title: "Custom1", action: #selector(MyAppViewController.custome1Method)) let custom2Item = UIMenuItem(title: "Custom2", action: #selector(MyAppViewController.custome2Method)) editMenu.menuItems = [custom1Item, custom2Item] editMenu.setTargetRect(CGRect(x: point.x, y: point.y, width: 20, height: 20), in: self) editMenu.setMenuVisible(true, animated: true) } } override var canBecomeFirstResponder: Bool { return true } } class MyAppViewController: UIViewController { override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { // You need to only return true for the actions you want, otherwise you get the whole range of // iOS actions. You can see this by just removing the if statement here. //For folder edit if action == #selector(MyAppViewController.custome1Method) { return true } if action == #selector(MyAppViewController.custome2Method) { return true } return false } } 
+2
source

In iOS 9 with Swift , to SHOW ONLY CUSTOMS ITEMS (without cutting by default, pasting, etc.), I managed to work only with the following code.

In the viewDidLoad method:

 let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(contextMenuHandler)) longPressRecognizer.minimumPressDuration = 0.3 longPressRecognizer.delaysTouchesBegan = true self.collectionView?.addGestureRecognizer(longPressRecognizer) 

Override method canBecomeFirstResponder :

 override func canBecomeFirstResponder() -> Bool { return true } 

Override these two related methods:

 override func collectionView(collectionView: UICollectionView, shouldShowMenuForItemAtIndexPath indexPath: NSIndexPath) -> Bool { return true } override func collectionView(collectionView: UICollectionView, canPerformAction action: Selector, forItemAtIndexPath indexPath: NSIndexPath, withSender sender: AnyObject?) -> Bool { return (action == #selector(send) || action == #selector(delete)) } 

Create a gesture handler method:

 func contextMenuHandler(gesture: UILongPressGestureRecognizer) { if gesture.state == UIGestureRecognizerState.Began { let indexPath = self.collectionView?.indexPathForItemAtPoint(gesture.locationInView(self.collectionView)) if indexPath != nil { self.selectedIndexPath = indexPath! let cell = self.collectionView?.cellForItemAtIndexPath(self.selectedIndexPath) let menu = UIMenuController.sharedMenuController() let sendMenuItem = UIMenuItem(title: "Send", action: #selector(send)) let deleteMenuItem = UIMenuItem(title: "Delete", action: #selector(delete)) menu.setTargetRect(CGRectMake(0, 5, 60, 80), inView: (cell?.contentView)!) menu.menuItems = [sendMenuItem, deleteMenuItem] menu.setMenuVisible(true, animated: true) } } } 

And finally, create selection methods:

 func send() { print("Send performed!") } func delete() { print("Delete performed!") } 

Hope this helps. :)

Greetings.

0
source

Source: https://habr.com/ru/post/946956/


All Articles