在 UICollectionView 中复制标注
Posted
技术标签:
【中文标题】在 UICollectionView 中复制标注【英文标题】:Copy Callout in UICollectionView 【发布时间】:2012-11-19 16:54:59 【问题描述】:我在每个单元格中都有一个带有 UIImageView 的 UICollectionView,现在我想添加 Copy Callout,就像在 Photos.app 中一样:
我在 UICollectionViewDelegate 中看到了这个方法:
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath
return YES;
经过几分钟的研究,我发现了 UIMenuController 类,据我了解,我必须使用它来获取菜单,但无论如何,我认为必须有更简单的方法,然后创建 UIGestureRecognizer,并创建、定位等等我的 UIMenu。
我在正确的轨道上吗?您如何实现此功能?
【问题讨论】:
【参考方案1】:是的,你在正确的轨道上。您还可以使用此技术实现剪切、复制、粘贴之外的自定义操作。
Custom actions for the UICollectionView
// ViewController.h
@interface ViewController : UICollectionViewController
// ViewController.m
-(void)viewDidLoad
[super viewDidLoad];
self.collectionView.delegate = self;
UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:@"Custom Action"
action:@selector(customAction:)];
[[UIMenuController sharedMenuController] setMenuItems:[NSArray arrayWithObject:menuItem]];
#pragma mark - UICollectionViewDelegate methods
- (BOOL)collectionView:(UICollectionView *)collectionView
canPerformAction:(SEL)action
forItemAtIndexPath:(NSIndexPath *)indexPath
withSender:(id)sender
return YES; // YES for the Cut, copy, paste actions
- (BOOL)collectionView:(UICollectionView *)collectionView
shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath
return YES;
- (void)collectionView:(UICollectionView *)collectionView
performAction:(SEL)action
forItemAtIndexPath:(NSIndexPath *)indexPath
withSender:(id)sender
NSLog(@"performAction");
#pragma mark - UIMenuController required methods
- (BOOL)canBecomeFirstResponder
// NOTE: The menu item will on ios 6.0 without YES (May be optional on iOS 7.0)
return YES;
- (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;
#pragma mark - Custom Action(s)
- (void)customAction:(id)sender
NSLog(@"custom action! %@", sender);
注意:iOS 7.0 更改行为
在您的 UICollectionViewCell 子类中,您需要添加自定义操作方法,否则不会出现任何内容。
// Cell.m
#import "Cell.h"
@implementation Cell
- (id)initWithFrame:(CGRect)frame
self = [super initWithFrame:frame];
if (self)
// custom logic
return self;
- (void)customAction:(id)sender
NSLog(@"Hello");
if([self.delegate respondsToSelector:@selector(customAction:forCell:)])
[self.delegate customAction:sender forCell:self];
@end
您需要创建一个委托协议并将其设置在每个单元格上,以回调维护您的 UICollectionView 的 UIController。这是因为单元格不应该与您的模型无关,因为它只涉及显示内容。
// Cell.h
#import <UIKit/UIKit.h>
@class Cell; // Forward declare Custom Cell for the property
@protocol MyMenuDelegate <NSObject>
@optional
- (void)customAction:(id)sender forCell:(Cell *)cell;
@end
@interface Cell : UICollectionViewCell
@property (strong, nonatomic) UILabel* label;
@property (weak, nonatomic) id<MyMenuDelegate> delegate;
@end
在您的 ViewController 或 UICollectionViewController 的子类中,您需要遵守协议并实现新方法。
// ViewController.m
@interface ViewController () <MyMenuDelegate>
@end
// @implementation ViewController ...
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath;
Cell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"MY_CELL" forIndexPath:indexPath];
cell.delegate = self;
return cell;
// ...
// Delegate method for iOS 7.0 to get action from UICollectionViewCell
- (void)customAction:(id)sender forCell:(Cell *)cell
NSLog(@"custom action! %@", sender);
可选:在 UIView 子类中,如果您在此处实现方法 canPerformAction,而不是在 UIViewController 中,则可以覆盖默认的剪切、复制、粘贴。否则,该行为将在您的自定义方法之前显示默认方法。
// Cell.m
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
NSLog(@"canPerformAction");
// The selector(s) should match your UIMenuItem selector
NSLog(@"Sender: %@", sender);
if (action == @selector(customAction:))
return YES;
return NO;
【讨论】:
customAction 中,sender 是 UIMenuController。您如何从中获得对单元格的引用? @drhr 你找到解决方案了吗? 看起来这个 *** 有助于 iOS 7。需要在自定义单元格中调用:***.com/questions/18906991/… 对于 iOS 7,这是过度设计的。这要简单得多:在***.com/a/19232074/341994 上查看我的完整代码答案 @PaulSolt :解决了我的问题,我从三四天开始就一直在尝试! Insort,感谢您节省了我的时间。【参考方案2】:这是完整的解决方案:
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath
return YES;
- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
if ([NSStringFromSelector(action) isEqualToString:@"copy:"])
return YES;
else
return NO;
- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
if ([NSStringFromSelector(action) isEqualToString:@"copy:"])
UIPasteboard *pasteBoard = [UIPasteboard pasteboardWithName:UIPasteboardNameGeneral create:NO];
pasteBoard.persistent = YES;
NSData *capturedImageData = UIImagePNGRepresentation([_capturedPhotos objectAtIndex:indexPath.row]);
[pasteBoard setData:capturedImageData forPasteboardType:(NSString *)kUTTypePNG];
就我而言,我在 CollectionView 中只允许复制功能,如果按下复制,我会将单元格内的图像复制到粘贴板。
【讨论】:
是的,你只需要这 3 个方法。其他的都是不必要的。 谢谢,我试过你的解决方案,同时写 UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:@"Edit" action:@selector(editPlate:)];但是,它需要我有一个方法 editPlate 但我想使用 performAction,所以我可以知道单元格 ID。如何定位菜单项?【参考方案3】:可能有点晚了,但我可能为那些仍在寻找这个的人找到了更好的解决方案:
在 UICollectionViewController 的 viewDidLoad 中添加您的项目:
UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:@"Title" action:@selector(action:)];
[[UIMenuController sharedMenuController] setMenuItems:[NSArray arrayWithObject:menuItem]];
添加以下委托方法:
//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;
子类 UICollectionViewCell 如果你还没有。添加您为项目指定的方法:
- (void)action:(UIMenuController*)menuController
这样你就不需要任何 becomeFirstResponder 或其他方法。您可以将所有操作集中在一个地方,如果您以单元格本身作为参数调用通用方法,则可以轻松处理不同的单元格。
编辑:uicollectionview 不知何故需要此方法的存在(您的自定义操作不会调用此方法,我认为 uicollectionview 只是检查是否存在)
- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
【讨论】:
@ThinkBonobo 感谢您的评论,但实际上这是我的编辑,他只是改进了英语;-)。 我发现这个答案特别有用。但是,我仍然必须最终创建一个引用回集合控制器的委托,以便我可以刷新集合控制器。另外,注意编辑。需要那个空的 performAction 类。以上是关于在 UICollectionView 中复制标注的主要内容,如果未能解决你的问题,请参考以下文章
如何删除 UICollectionView 中单元格之间的空间? [复制]