Копировать выноску в UICollectionView
У меня есть UICollectionView с UIImageView в каждой ячейке, теперь я хочу добавить Copy Callout, как в Photos.app:
Я видел этот метод в UICollectionViewDelegate:
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
После нескольких дополнительных минут исследования я обнаружил, что класс UIMenuController, как я понял, должен работать с ним, чтобы получить меню, но в любом случае я думаю, что должен быть более простой способ, чем создание UIGestureRecognizer, создание, позиционирование и т. Д. UIMenu.
Я на правильном пути? Как вы могли бы реализовать эту функцию?
3 ответа
Это полное решение:
- (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, и если копия нажата, я копирую изображение, которое находится внутри ячейки, в PasteBoard.
Да, вы на правильном пути. Вы также можете реализовать пользовательские действия помимо вырезания, копирования, вставки, используя эту технику.
Пользовательские действия для 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
Вам нужно будет создать протокол делегата и установить его в каждой ячейке для обратного вызова UIController, который поддерживает ваш UICollectionView. Это связано с тем, что ячейка не должна иметь ничего общего с вашей моделью, поскольку она участвует только в отображении контента.
// 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 вы можете переопределить стандартные параметры Cut, Copy, Paste, если реализуете здесь метод 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; }
Может быть, немного поздно, но я, возможно, нашел лучшее решение для тех, кто все еще ищет это:
В viewDidLoad вашего UICollectionViewController добавьте ваш элемент:
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 {
}
Таким образом, вам не нужны никакие другие методы, которые вам нужны, или вам не нужно ставить статистику. У вас есть все действия в одном месте, и вы можете легко обрабатывать разные ячейки, если вы вызываете общий метод с самой ячейкой в качестве параметра.
Изменить: Каким-то образом uicollectionview нуждается в существовании этого метода (этот метод не вызывается для ваших пользовательских действий, я думаю, что uicollectionview просто проверяет наличие)
- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
}