Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
246 views
in Technique[技术] by (71.8m points)

ios - Copy Callout in UICollectionView

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

enter image description here

I saw this method in the UICollectionViewDelegate:

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

After few additional minutes of research I found UIMenuController Class, as I understood, I must to work with it to get the Menu, but anyway, I think that there must to be more simple way then creating UIGestureRecognizer, and creating, positioning etc. my UIMenu.

Am I on the right track? How could you implement this feature?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Yes you're on the right track. You can also implement custom actions beyond cut, copy, paste using this technique.

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

Note: iOS 7.0 Changes Behavior

  1. In your UICollectionViewCell subclass you'll need to add the custom 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
    
  2. You'll need to create a delegate protocol and set it on every cell to call back to the UIController that maintains your UICollectionView. This is because the cell should no nothing about your model, since it only is involved in displaying 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
    
  3. In your ViewController or Subclass of UICollectionViewController you'll need to conform to the protocol and implement the 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

  4. Optional: In your UIView Subclass you can override the default Cut, Copy, Paste if you implement the method canPerformAction here, rather than in the UIViewController. Otherwise the behavior will show the default methods 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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...