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 【问题描述】:我正在使用UIMenuItem
在UICollectionView
单元格长按中执行自定义操作。
这与 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 中显示的主要内容,如果未能解决你的问题,请参考以下文章