Copy callout to UICollectionView

I have a UICollectionView with a UIImageView in each cell, now I want to add Copy Callout, as in Photos.app:

enter image description here

I saw this method in a UICollectionViewDelegate:

- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath { return YES; } 

After a few additional minutes of research, I found the UIMenuController class, as I understand it, I need to work with it to get the menu, but in any case, I think that there should be an easier way to create a UIGestureRecognizer and create positioning, etc. my UIMenu.

Am I on the right track? How to implement this function?

+8
ios ios5 ios6 uicollectionview uicollectionviewcell
source share
3 answers

This is the complete solution:

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

In my case, I only allow the copy function in my CollectionView, and if the copy is clicked, I copy the image inside the cell to the PasteBoard.

+9
source share

Yes, you're on the right track. You can also implement custom actions besides cutting, copying and pasting using this technique.

Custom Actions for 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); } 

Note: iOS 7.0 changes the behavior.

  • In your subclass of UICollectionViewCell, you will need to add special action methods or nothing will appear.

     // 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 
  • You will need to create a delegate protocol and set it in each cell in order to call back to the UIController that supports your UICollectionView. This is because the cell should not have anything to do with your model, since it only participates in the display of content.

     // 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 
  • In your ViewController or subclass of UICollectionViewController, you will need to conform to the protocol and implement a new method.

     // 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); } 

    Custom longpress action menu for UICollectionView

  • Optional . In your UIView subclass, you can override the standard Cut, Copy, Paste if you implement the canPerformAction method here, and not in the UIViewController. Otherwise, the behavior will be displayed by default before your custom methods.

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

    Custom Action from UICell canPerformAction

+18
source share

Maybe a little late, but I may have found a better solution for those who are still looking for this:

In the viewDidLoad of your UICollectionViewController add your element:

 UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:@"Title" action:@selector(action:)]; [[UIMenuController sharedMenuController] setMenuItems:[NSArray arrayWithObject:menuItem]]; 

Add the following delegate methods:

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

Subclass of UICollectionViewCell, if you haven’t already. Add the method that you specified for your element:

 - (void)action:(UIMenuController*)menuController { } 

Thus, you do not need any startFirstResponder method or other methods. You have all the actions in one place, and you can easily handle different cells if you call a common method with the cell itself as a parameter.

Edit: somehow uicollectionview needs this method to exist (this method is not called for your custom action, I think uicollectionview just checks for availability)

 - (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { } 
+5
source share

All Articles