简体   繁体   English

UICollectionViewCell 上的长按手势

[英]Long press gesture on UICollectionViewCell

I was wondering how to add a long press gesture recognizer to a (subclass of) UICollectionView.我想知道如何向 UICollectionView(的子类)添加长按手势识别器。 I read in the documentation that it is added by default, but I can't figure out how.我在文档中读到它是默认添加的,但我不知道如何添加。

What I want to do is: Long press on a cell ( I have a calendar thingy from github ), get which cell is tapped and then do stuff with it.我想要做的是:长按一个单元格(我有一个来自 github 的日历),获取哪个单元格被点击,然后用它做一些事情。 I need to know what cell is longpressed.我需要知道长按什么单元格。 Sorry for this broad question, but i couldn't find anything better on either google or SO很抱歉这个广泛的问题,但我在谷歌或 SO 上找不到更好的东西

Objective-C目标-C

In your myCollectionViewController.h file add the UIGestureRecognizerDelegate protocolmyCollectionViewController.h文件中添加UIGestureRecognizerDelegate协议

@interface myCollectionViewController : UICollectionViewController<UIGestureRecognizerDelegate>

in your myCollectionViewController.m file:在您的myCollectionViewController.m文件中:

- (void)viewDidLoad
{
    // attach long press gesture to collectionView
    UILongPressGestureRecognizer *lpgr 
       = [[UILongPressGestureRecognizer alloc]
                     initWithTarget:self action:@selector(handleLongPress:)];
    lpgr.delegate = self;
    lpgr.delaysTouchesBegan = YES;
    [self.collectionView addGestureRecognizer:lpgr];
}

-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer.state != UIGestureRecognizerStateEnded) {
        return;
    }
    CGPoint p = [gestureRecognizer locationInView:self.collectionView];

    NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:p];
    if (indexPath == nil){
        NSLog(@"couldn't find index path");            
    } else {
        // get the cell at indexPath (the one you long pressed)
        UICollectionViewCell* cell =
        [self.collectionView cellForItemAtIndexPath:indexPath];
        // do stuff with the cell
    }
}

Swift迅速

class Some {

    @objc func handleLongPress(gesture : UILongPressGestureRecognizer!) {
        if gesture.state != .Ended {
            return
        }
        let p = gesture.locationInView(self.collectionView)

        if let indexPath = self.collectionView.indexPathForItemAtPoint(p) {
            // get the cell at indexPath (the one you long pressed)
            let cell = self.collectionView.cellForItemAtIndexPath(indexPath)
            // do stuff with the cell
        } else {
            print("couldn't find index path")
        }
    }
}

let some = Some()
let lpgr = UILongPressGestureRecognizer(target: some, action: #selector(Some.handleLongPress))

Swift 4斯威夫特 4

class Some {

    @objc func handleLongPress(gesture : UILongPressGestureRecognizer!) {
        if gesture.state != .ended { 
            return 
        } 

        let p = gesture.location(in: self.collectionView) 

        if let indexPath = self.collectionView.indexPathForItem(at: p) { 
            // get the cell at indexPath (the one you long pressed) 
            let cell = self.collectionView.cellForItem(at: indexPath) 
            // do stuff with the cell 
        } else { 
            print("couldn't find index path") 
        }
    }
}

let some = Some()
let lpgr = UILongPressGestureRecognizer(target: some, action: #selector(Some.handleLongPress))

The same code @abbood's code for Swift:与@abbood 的 Swift 代码相同的代码:

In viewDidLoad:在 viewDidLoad 中:

let lpgr : UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "handleLongPress:")
lpgr.minimumPressDuration = 0.5
lpgr.delegate = self
lpgr.delaysTouchesBegan = true
self.collectionView?.addGestureRecognizer(lpgr)

And the function:和功能:

func handleLongPress(gestureRecognizer : UILongPressGestureRecognizer){

    if (gestureRecognizer.state != UIGestureRecognizerState.Ended){
        return
    }

    let p = gestureRecognizer.locationInView(self.collectionView)

    if let indexPath : NSIndexPath = (self.collectionView?.indexPathForItemAtPoint(p))!{
        //do whatever you need to do
    }

}

Do not forget the delegate UIGestureRecognizerDelegate不要忘记委托UIGestureRecognizerDelegate

Use the delegate of UICollectionView receive long press event使用 UICollectionView 的委托接收长按事件

You must impl 3 method below.您必须在下面实现 3 种方法。

//UICollectionView menu delegate
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath{

   //Do something

   return YES;
}
- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender{
    //do nothing
    return NO;
}

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

Swift 5:斯威夫特 5:

private func setupLongGestureRecognizerOnCollection() {
    let longPressedGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(gestureRecognizer:)))
    longPressedGesture.minimumPressDuration = 0.5
    longPressedGesture.delegate = self
    longPressedGesture.delaysTouchesBegan = true
    collectionView?.addGestureRecognizer(longPressedGesture)
}

@objc func handleLongPress(gestureRecognizer: UILongPressGestureRecognizer) {
    if (gestureRecognizer.state != .began) {
        return
    }

    let p = gestureRecognizer.location(in: collectionView)

    if let indexPath = collectionView?.indexPathForItem(at: p) {
        print("Long press at item: \(indexPath.row)")
    }
}

Also don't forget to implement UIGestureRecognizerDelegate and call setupLongGestureRecognizerOnCollection from viewDidLoad or wherever you need to call it.另外不要忘记实现 UIGestureRecognizerDelegate 并从 viewDidLoad 或任何你需要调用它的地方调用 setupLongGestureRecognizerOnCollection 。

Answers here to add a custom longpress gesture recognizer are correct however according to the documentation here : the parent class of UICollectionView class installs a default long-press gesture recognizer to handle scrolling interactions so you must link your custom tap gesture recognizer to the default recognizer associated with your collection view.此处添加自定义长按手势识别器的答案是正确的,但是根据此处的文档: UICollectionView类的父类安装了default long-press gesture recognizer来处理滚动交互,因此您必须将自定义点击手势识别器链接到关联的默认识别器使用您的集合视图。

The following code will avoid your custom gesture recognizer to interfere with the default one:以下代码将避免您的自定义手势识别器干扰默认识别器:

UILongPressGestureRecognizer* longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGesture:)];

longPressGesture.minimumPressDuration = .5; //seconds
longPressGesture.delegate = self;

// Make the default gesture recognizer wait until the custom one fails.
for (UIGestureRecognizer* aRecognizer in [self.collectionView gestureRecognizers]) {
   if ([aRecognizer isKindOfClass:[UILongPressGestureRecognizer class]])
      [aRecognizer requireGestureRecognizerToFail:longPressGesture];
} 
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];

[cell addGestureRecognizer:longPress];

and add the method like this.并添加这样的方法。

- (void)longPress:(UILongPressGestureRecognizer*)gesture
{
    if ( gesture.state == UIGestureRecognizerStateEnded ) {

        UICollectionViewCell *cellLongPressed = (UICollectionViewCell *) gesture.view;
    }
}

To have an external gesture recognizer and do not conflict with internal gesture recognizers on the UICollectionView you need to:要拥有外部手势识别器并且不与 UICollectionView 上的内部手势识别器冲突,您需要:

Add your gesture recognizer, set up it and capture a reference for it somewhere (the best option is on your subclass if you subclassed UICollectionView)添加你的手势识别器,设置它并在某处捕获它的引用(如果你子类化 UICollectionView,最好的选择是在你的子类上)

@interface UICollectionViewSubclass : UICollectionView <UIGestureRecognizerDelegate>    

@property (strong, nonatomic, readonly) UILongPressGestureRecognizer *longPressGestureRecognizer;   

@end

Override default initialization methods initWithFrame:collectionViewLayout: and initWithCoder: and add set up method for you long press gesture recognizer覆盖默认初始化方法initWithFrame:collectionViewLayout:initWithCoder:并为您添加长按手势识别器的设置方法

@implementation UICollectionViewSubclass

-(instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout
{
    if (self = [super initWithFrame:frame collectionViewLayout:layout]) {
        [self setupLongPressGestureRecognizer];
    }
    return self;
}

-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super initWithCoder:aDecoder]) {
        [self setupLongPressGestureRecognizer];
    }
    return self;
}

@end

Write your setup method so it instantiates long press gesture recognizer, set it's delegate, setup dependencies with UICollectionView gesture recognizer (so it be the main gesture and all other gestures will wait till that gesture fails before being recognized) and add gesture to the view编写您的设置方法,以便它实例化长按手势识别器,设置它的委托,使用 UICollectionView 手势识别器设置依赖项(因此它是主要手势,所有其他手势将等到该手势失败后再被识别)并将手势添加到视图

-(void)setupLongPressGestureRecognizer
{
    _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self
                                                                                action:@selector(handleLongPressGesture:)];
    _longPressGestureRecognizer.delegate = self;

    for (UIGestureRecognizer *gestureRecognizer in self.collectionView.gestureRecognizers) {
        if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) {
            [gestureRecognizer requireGestureRecognizerToFail:_longPressGestureRecognizer];
        }
    }

    [self.collectionView addGestureRecognizer:_longPressGestureRecognizer];
}

Also don't forget to implement UIGestureRecognizerDelegate methods that fails that gesture and allow simultaneous recognition (you may or may don't need to implement it, it depends on other gesture recognizers you have or dependencies with internal gesture recognizers)另外不要忘记实现使该手势失败并允许同时识别的 UIGestureRecognizerDelegate 方法(您可能需要也可能不需要实现它,这取决于您拥有的其他手势识别器或与内部手势识别器的依赖关系)

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    if ([self.longPressGestureRecognizer isEqual:gestureRecognizer]) {
        return NO;
    }

    return NO;
}

credentials for that goes to internal implementation of LXReorderableCollectionViewFlowLayout用于LXReorderableCollectionViewFlowLayout 的内部实现的 凭据

Perhaps, using UILongPressGestureRecognizer is the most widespread solution.也许,使用UILongPressGestureRecognizer是最普遍的解决方案。 But I encounter with it two annoying troubles:但是我遇到了两个烦人的麻烦:

  • sometimes this recognizer works in incorrect way when we're moving our touch;有时,当我们移动触摸时,此识别器的工作方式不正确;
  • recognizer intercepts other touch actions so we can't use highlight callbacks of our UICollectionView in a proper way.识别器会拦截其他触摸操作,因此我们无法以正确的方式使用 UICollectionView 的高亮回调。

Let me suggest one a little bit bruteforce, but working as it's required suggestion:让我建议一点点蛮力,但按要求工作:

Declaring a callback description for long click on our cell:声明长按单元格的回调描述:

typealias OnLongClickListener = (view: OurCellView) -> Void

Extending UICollectionViewCell with variables (we can name it OurCellView, for example):使用变量扩展UICollectionViewCell (例如,我们可以将其命名为 OurCellView):

/// To catch long click events.
private var longClickListener: OnLongClickListener?

/// To check if we are holding button pressed long enough.
var longClickTimer: NSTimer?

/// Time duration to trigger long click listener.
private let longClickTriggerDuration = 0.5

Adding two methods in our cell class:在我们的单元类中添加两个方法:

/**
 Sets optional callback to notify about long click.

 - Parameter listener: A callback itself.
 */
func setOnLongClickListener(listener: OnLongClickListener) {
    self.longClickListener = listener
}

/**
 Getting here when long click timer finishs normally.
 */
@objc func longClickPerformed() {
    self.longClickListener?(view: self)
}

And overriding touch events here:并在此处覆盖触摸事件:

/// Intercepts touch began action.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    longClickTimer = NSTimer.scheduledTimerWithTimeInterval(self.longClickTriggerDuration, target: self, selector: #selector(longClickPerformed), userInfo: nil, repeats: false)
    super.touchesBegan(touches, withEvent: event)
}

/// Intercepts touch ended action.
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
    longClickTimer?.invalidate()
    super.touchesEnded(touches, withEvent: event)
}

/// Intercepts touch moved action.
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
    longClickTimer?.invalidate()
    super.touchesMoved(touches, withEvent: event)
}

/// Intercepts touch cancelled action.
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
    longClickTimer?.invalidate()
    super.touchesCancelled(touches, withEvent: event)
}

Then somewhere in controller of our collection view declaring callback listener:然后在我们的集合视图的控制器中的某个地方声明回调侦听器:

let longClickListener: OnLongClickListener = {view in
    print("Long click was performed!")
}

And finally in cellForItemAtIndexPath setting callback for our cells:最后在cellForItemAtIndexPath中为我们的单元格设置回调:

/// Data population.
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath)
    let castedCell = cell as? OurCellView
    castedCell?.setOnLongClickListener(longClickListener)

    return cell
}

Now we can intercept long click actions on our cells.现在我们可以拦截我们单元格上的长按操作。

Easier solution.更简单的解决方案。

In your cellForItemAt delegate (set .tag property for later):在您的cellForItemAt委托中(稍后设置 .tag 属性):

cell.gestureRecognizers?.removeAll()
cell.tag = indexPath.row
let directFullPreviewer = UILongPressGestureRecognizer(target: self, action: #selector(directFullPreviewLongPressAction))
cell.addGestureRecognizer(directFullPreviewer)

And callback for longPress:和 longPress 回调:

@objc func directFullPreviewLongPressAction(g: UILongPressGestureRecognizer)
{
    if g.state == UIGestureRecognizer.State.began
    {
        // Get index as g.view.tag and that's it
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM