Search code examples
objective-cuicollectionviewios7uimenucontroller

UIMenuController sharedMenuController - custom menuitem for uicollectionview do not show in ios 7


I'm using a UIMenuItem to perform a custom action in UICollectionView cell long press. this worked perfectly with iOS 6, but now I am converting my application to iOS 7 and Xcode 5 and it don't work. The custom item do not shown.

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;
}

Solution

  • I don't know about iOS 6, but in iOS 7 it's very simple. You just need the three standard collection view delegate menu-handling methods, plus an action method in the cell subclass. There is no need to play with first responder or anything like that. So, for example (in this example, Copy is a standard item but Capital is something I've 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];
    }