UIMenuController sharedMenuController - uicollectionview 的自定义菜单项不在 ios 7 中显示

Posted

技术标签:

【中文标题】UIMenuController sharedMenuController - uicollectionview 的自定义菜单项不在 ios 7 中显示【英文标题】:UIMenuController sharedMenuController - custom menuitem for uicollectionview do not show in ios 7 【发布时间】:2013-09-20 00:13:48 【问题描述】:

我正在使用UIMenuItemUICollectionView 单元格长按中执行自定义操作。 这与 ios 6 完美配合,但现在我将我的应用程序转换为 iOS 7 和 Xcode 5,但它不起作用。自定义项目未显示。

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;

【问题讨论】:

完全相同的问题。我发现现在自定义单元格正在成为菜单的第一响应者。因此,如果您在自定义单元格上实现 CanPerformAction 和自定义操作选择器,它就可以工作。但是,我更愿意将选择器保留在视图控制器上。 是的,阿布拉莫夫,你是对的。我在自定义单元上实现并工作,但更喜欢在视图控制器上保留选择器。您建议在将选择器保留在 VC 中的同时使用此功能吗?也许委托一个协议... 这是一个解决方案,但不是很优雅。我一直在努力寻找更好的。 向单元格中的集合 VC 添加一个委托,导致集合 VC 永远不会释放(参见代码作为答案)。从长远来看,我们需要找到更好的解决方案。 【参考方案1】:

我不了解 iOS 6,但在 iOS 7 中它非常简单。您只需要三个标准的集合视图委托菜单处理方法,以及单元子类中的一个操作方法。无需与第一响应者或类似的东西一起玩。因此,例如(在本例中,Copy 是标准项目,但 Capital 是我添加到菜单中的项目):

// 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];

【讨论】:

不错的替代方案,无需 iOS 6.0 中的额外内容即可工作。我认为封装行为以在一种方法中查找 UICollectionView 以重用于多个操作会更好。查找失败时是否有可能? 没有。您知道的一件事是,单元格在某种程度上是集合视图的子视图。如果不是,它就不是第一响应者,因此一开始就不会收到此消息。 — 显然,我展示的 entire 方法应该被抽象为多个操作;它是完全通用的(注意使用_cmd)。 单元格中菜单的 x 位置如何自定义? @NavaCarmon 请不要使用 cmets 来获取。如果您有新问题,请提出新问题。 @matt 我想邀请你回答这个问题***.com/questions/31183894/…【参考方案2】:

我用类似 *** 问题中的图像和示例更新了我的 iOS 7.0 解决方案。

我对委托使用 ARC 和弱引用。似乎适用于 iOS 6.0 和 iOS 7.0

https://***.com/a/13618212/276626

【讨论】:

【参考方案3】:

我在 UICollectionView 上添加了一个 searchBar,UIMenuController 的结果是 MIA。经过两天的反复试验,我认为终于找到了一种方法。

问题(在 iOS7 上运行):

    UICollectionViewController 上的 UILongPressGestureRecognizer 被调用 MenuController 显示 添加了一个 UISearchBar 并在 collectionView 重新加载其数据时:不再有菜单控制器

认为让它发挥作用的诀窍是从搜索栏中显式删除第一响应者状态

- (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];
    

当然控制器中也有这些响应者状态方法:

- (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];

【讨论】:

辞去 firstResponder 是解决内容“焦点”竞争的最佳方式。【参考方案4】:

正如 nicolas 所说,相关代码如下。注意:这会导致集合 VC 永远不会释放(意味着永远不会调用 dealloc)。从长远来看,我们需要找到更好的解决方案,或者直到 Apple 修复这个 iOS 7.x 错误。

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

在 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];




在集合VC.h中

@interface VC : UIViewController <

                        CellToVCDelegate>

在 VC.m 中:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

    NibCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cvCellIdentifier
                                                              forIndexPath:indexPath];

cell.delegate = self;
return cell;

【讨论】:

它永远不会解除分配,因为您的单元格有一个对其委托(视图控制器)的strong 引用,并且视图控制器对单元格有一个强引用。 delegate 属性应该是 weak(你也不需要声明实例变量)。 对于非弧:更改了保留以分配 @property (nonatomic) id 委托;

以上是关于UIMenuController sharedMenuController - uicollectionview 的自定义菜单项不在 ios 7 中显示的主要内容,如果未能解决你的问题,请参考以下文章

UIMenuController 没有出现

UIMenuController

UIMenuController

使用 uimenucontroller 时遇到问题

使用按钮单击显示 UIMenuController

确定何时解除 UIMenuController?