Copier la légende dans UICollectionView

J'ai une UICollectionView avec UIImageView dans chaque cellule, maintenant je veux append une légende de copy, comme dans Photos.app:

entrez la description de l'image ici

J'ai vu cette méthode dans le UICollectionViewDelegate:

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

Après quelques minutes supplémentaires de search j'ai trouvé UIMenuController Class, comme je l'ai compris, je dois travailler avec pour get le Menu, mais de toute façon, je pense qu'il doit y avoir plus simple moyen de créer UIGestureRecognizer, et créer, positionner etc. UIMenu.

Suis-je sur la bonne voie? Comment pourriez-vous implémenter cette fonctionnalité?

Oui, vous êtes sur la bonne voie. Vous pouvez également implémenter des actions personnalisées au-delà de couper, copyr, coller en utilisant cette technique.

Actions personnalisées pour 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); } 

Remarque: iOS 7.0 change de comportement

  1. Dans votre sous-class UICollectionViewCell, vous devrez append les methods d'action personnalisées, ou rien ne s'affichera.

     // 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. Vous devrez créer un protocole délégué et le définir sur chaque cellule pour callbacker l'UIController qui gère votre UICollectionView. C'est parce que la cellule ne doit rien avoir à propos de votre model, puisqu'elle est seulement impliquée dans l'affichage du contenu.

     // 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. Dans votre ViewController ou sous-class de UICollectionViewController, vous devrez vous conformer au protocole et implémenter la nouvelle méthode.

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

    Menu d'action personnalisé longpress pour UICollectionView

  4. Facultatif : Dans votre sous-class UIView, vous pouvez replace la valeur par défaut Couper, Copier, Coller si vous implémentez la méthode canPerformAction ici, plutôt que dans le UIViewController. Sinon, le comportement montrera les methods par défaut avant vos methods personnalisées.

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

    Action personnalisée de UICell canPerformAction

C'est la solution complète:

 - (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath { return YES; } - (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { if ([NSSsortingngFromSelector(action) isEqualToSsortingng:@"copy:"]) return YES; else return NO; } - (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { if ([NSSsortingngFromSelector(action) isEqualToSsortingng:@"copy:"]) { UIPasteboard *pasteBoard = [UIPasteboard pasteboardWithName:UIPasteboardNameGeneral create:NO]; pasteBoard.persistent = YES; NSData *capturedImageData = UIImagePNGRepresentation([_capturedPhotos objectAtIndex:indexPath.row]); [pasteBoard setData:capturedImageData forPasteboardType:(NSSsortingng *)kUTTypePNG]; } } 

Dans mon cas, je n'autorise que la fonction de copy dans mon CollectionView, et si la copy est pressée, je copy l'image qui est dans la cellule au PasteBoard.

Peut-être un peu en retard, mais j'ai peut-être trouvé une meilleure solution pour ceux qui sont encore à la search de ceci:

Dans viewDidLoad de votre UICollectionViewController ajoutez votre article:

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

Ajoutez les methods de délégué suivantes:

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

Sous-class UICollectionViewCell si vous ne l'avez pas déjà fait. Ajoutez la méthode que vous avez spécifiée pour votre article:

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

De cette façon, vous n'avez pas besoin de getFirstResponder ou d'autres methods. Vous avez toutes les actions en un seul endroit et vous pouvez facilement gérer des cellules différentes si vous appelez une méthode générale avec la cellule elle-même en tant que paramètre.

Edit: D'une certaine manière l'uicollectionview a besoin de l'existence de cette méthode (cette méthode n'est pas appelée pour votre action personnalisée, je pense que l'uicollectionview vérifie juste l'existence)

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