UIMenuController sharedMenuController - a custom menu item for uicollectionview is not displayed in ios 7

I use UIMenuItem to perform a custom action in a long-click UICollectionView cell. this worked fine with iOS 6, but now I am converting my application to iOS 7 and Xcode 5 and it does not work. Custom item is not displayed.

 UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:@"Unfavorite" action:@selector(unFavorite:)]; [[UIMenuController sharedMenuController] setMenuItems:[NSArray arrayWithObject:menuItem]]; [UIMenuController sharedMenuController].menuVisible = YES; - (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender; { //do not show default itens like copy, paste.... [self becomeFirstResponder]; return NO; } - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { // The selector(s) should match your UIMenuItem selector if (action == @selector(unFavorite:)) { return YES; } return NO; } - (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { } - (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath { myIndexPath = indexPath; return YES; } 
+6
objective-c ios7 uicollectionview uimenucontroller
source share
4 answers

I do not know about iOS 6, but in iOS 7 it is very simple. You just need three standard methods for handling delegates in the collection view, as well as an action method in the cell subclass. No need to play with the first responder or anything like that. So, for example (in this example, Copy is the standard element, but Capital is what I added to the menu):

 // collection view delegate: - (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath { UIMenuItem* mi = [[UIMenuItem alloc] initWithTitle:@"Capital" action:NSSelectorFromString(@"capital:")]; [[UIMenuController sharedMenuController] setMenuItems:@[mi]]; return YES; } - (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { return (action == NSSelectorFromString(@"copy:") || action == NSSelectorFromString(@"capital:")); } - (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { // in real life, would do something here NSString* state = (self.sectionData)[indexPath.section][indexPath.row]; if (action == NSSelectorFromString(@"copy:")) NSLog(@"copying %@", state); else if (action == NSSelectorFromString(@"capital:")) NSLog(@"fetching the capital of %@", state); } // cell subclass: -(void)capital:(id)sender { // find my collection view UIView* v = self; do { v = v.superview; } while (![v isKindOfClass:[UICollectionView class]]); UICollectionView* cv = (UICollectionView*) v; // ask it what index path we are NSIndexPath* ip = [cv indexPathForCell:self]; // talk to its delegate if (cv.delegate && [cv.delegate respondsToSelector: @selector(collectionView:performAction:forItemAtIndexPath:withSender:)]) [cv.delegate collectionView:cv performAction:_cmd forItemAtIndexPath:ip withSender:sender]; } 
+8
source share

I updated my solution for iOS 7.0 with images and examples in a similar StackOverflow issue.

I use ARC and weak links for the delegate. Seems to work on both iOS 6.0 and iOS 7.0

stack overflow

+1
source share

I added a searchBar to my UICollectionView with the UIMenuController result going to the MIA. After two days of trial and error, I think I finally found a way to make it work.

Problem (works on iOS7):

  • UILongPressGestureRecognizer on a UICollectionViewController called
  • MenuController displays
  • Added UISearchBar , and as soon as collectionView reloads its data: there is no more menu manager

I think the trick to getting it to work is to explicitly remove the first responder status from the searchBar :

 - (void)longPressGestureDetected:(UILongPressGestureRecognizer *)gesture { if(gesture.state == UIGestureRecognizerStateBegan) { CGPoint touchPoint = [gesture locationInView:gesture.view]; NSInteger index = [self.collectionView indexPathForItemAtPoint:touchPoint].item; if(index >= 0 && index < self.documents.count) { // dismiss searchBar [self.searchBar resignFirstResponder]; [self becomeFirstResponder]; // select the right document //[self.documentManager selectDocumentWithIndex:index]; // show menu UIMenuController *menu = [UIMenuController sharedMenuController]; menu.menuItems = [self defaultMenuItems]; [menu setTargetRect:CGRectMake(touchPoint.x, touchPoint.y, 0, 0) inView:gesture.view]; [gesture.view becomeFirstResponder]; [menu update]; [menu setMenuVisible:YES animated:YES]; } } 

Of course, in the controller there are also such response status methods:

 - (BOOL)canBecomeFirstResponder { return YES; } - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { if(action == @selector(renameDocument:)) { return YES; } else { return NO; } } - (NSArray*)defaultMenuItems { // add menu items UIMenuItem *renameItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"Rename", @"Rename Menu Item") action:@selector(renameDocument:)]; return @[renameItem]; } 
+1
source share

as indicated by nicolas, the appropriate code follows. Note. This results in the VC collection never being released (this means that dealloc is never called). We need to find the best solution in the long run or until Apple fixes this iOS 7.x error.

at NibCell.h

 @protocol CellToVCDelegate <NSObject> @optional - (void)deleteActivity:(id)sender ; - (void)shareActivity:(id)sender; @end @interface NibCell : UICollectionViewCell{ id <CellToVCDelegate> delegate; } @property (nonatomic, retain) id <CellToVCDelegate> delegate; 

at NibCell.m

 #pragma mark - Custom Action(s) - (void)deleteActivity:(id)sender { NSLog(@"delete action! %@", sender); [self.delegate deleteActivity:sender]; } - (void)shareActivity:(id)sender { NSLog(@"shareActivity action! %@", sender); [self.delegate shareActivity:sender]; } 

in collection VC.h

 @interface VC : UIViewController < CellToVCDelegate> 

in VC.m:

 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { NibCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cvCellIdentifier forIndexPath:indexPath]; cell.delegate = self; return cell; } 
0
source share

All Articles