Копировать выноску в 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 меняет поведение

  1. В вашем подклассе 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
    
  2. Вам нужно будет создать протокол делегата и установить его в каждой ячейке для обратного вызова 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
    
  3. В вашем 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);
    }
    

    Настраиваемое длинное меню действий для UICollectionView

  4. Необязательно: В своем подклассе 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;
    }
    

    Пользовательское действие от UICell canPerformAction

Может быть, немного поздно, но я, возможно, нашел лучшее решение для тех, кто все еще ищет это:

В 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 {

}
Другие вопросы по тегам