在UICollectionViewCell上长按手势


108

我想知道如何将长按手势识别器添加到UICollectionView(的子类)中。我在文档中读到它是默认添加的,但我不知道如何添加。

我想要做的是:长按一个单元格(我有一个github上的日历东西),获取被点击的单元格,然后对其进行处理。我需要知道长时间压制的细胞。对不起这个广泛的问题,但是我在Google或SO上找不到更好的东西

Answers:


220

目标C

myCollectionViewController.h文件中添加UIGestureRecognizerDelegate协议

@interface myCollectionViewController : UICollectionViewController<UIGestureRecognizerDelegate>

在您的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
    }
}

迅速

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

斯威夫特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))

1
它已经在答案中:在这里UICollectionViewCell* cell = [self.collectionView cellForItemAtIndexPath:indexPath];参考希望所有这些都值得一个正确的答案奖:D
13年

10
对于(至少)ios7,您必须添加lpgr.delaysTouchesBegan = YES;以避免didHighlightItemAtIndexPath被首先触发。
DynamicDan 2014年

7
为什么要添加lpgr.delegate = self;?没有委托(您也未提供)也可以正常工作。
Yevhen Dubinin 2014年

3
@abbood答案有效,但是当长按识别器处于活动状态时,我无法在collectionview中上下滚动(使用另一只手指)。是什么赋予了?
PéturIngi Egilsson

4
就我个人而言,我会这样做UIGestureRecognizerStateBegan,因此手势是在识别时使用的,而不是在用户松开手指时使用的。
Jeffrey Sun

28

Swift的相同代码@abbood的代码:

在viewDidLoad中:

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

和功能:

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
    }

}

不要忘记代表 UIGestureRecognizerDelegate


3
效果很好,只是注意到“ handleLongPress:”应更改为#selector(YourViewController.handleLongPress(_ :))
Joseph Geraghty

16
效果很好,但是更改UIGestureRecognizerState.EndedUIGestureRecognizerState.Began是否要让代码在经过最短持续时间后才触发,而不只是在用户抬起手指时触发。
Crashalot '16

11

使用UICollectionView的委托接收长按事件

您必须在下面隐含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
}

注意:如果您为shouldShowMenuForItemAtIndexPath返回false,那么didSelectItemAtIndexPath也将被启动。当我想要长按和单按两种不同的动作时,这对我来说就成了问题。
ShannonS

它是现在不推荐使用的方法,您可以使用-(UIContextMenuConfiguration *)collectionView:(UICollectionView *)collectionView contextMenuConfigurationForItemAtIndexPath:(nonnull NSIndexPath *)indexPath point:(CGPoint)point;
Viktor Goltvyanitsa

8

答案在这里添加自定义长按手势识别是正确的根据文档在这里:父类的UICollectionView类安装了一个default long-press gesture recognizer用于处理滚动的互动,所以你必须自定义双击手势识别器链接到您的收藏视图关联的默认识别器。

以下代码将避免您的自定义手势识别器干扰默认手势识别器:

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

我明白你在说什么,但不是黑白的,文档说:The parent class of UICollectionView class installs a default tap gesture recognizer and a default long-press gesture recognizer to handle scrolling interactions. You should never try to reconfigure these default gesture recognizers or replace them with your own versions.所以默认的长按识别器是用于滚动的。这意味着它必须伴随着垂直移动。OP并没有在问关于这种行为,也不是他试图取代这种行为
大约

很抱歉,我听起来很防御,但是几个月以来我一直在iOS应用程序中使用以上代码。.想不到一次出现故障的消息
13年

@abbood很好。我不知道采购订单使用的第三方日历组件,但我认为即使您认为它永远不会发生,也可以防止故障;-)
tiguero

如果您必须等待默认识别器失败,这是否意味着会有延迟?
Crashalot '16

2
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];

[cell addGestureRecognizer:longPress];

并添加这样的方法。

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

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

2

要具有外部手势识别器并且不与UICollectionView上的内部手势识别器冲突,您需要:

添加您的手势识别器,进行设置并在某处捕获它的引用(如果您将UICollectionView子类化,则最好的选择在子类上)

@interface UICollectionViewSubclass : UICollectionView <UIGestureRecognizerDelegate>    

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

@end

覆盖默认的初始化方法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

编写您的设置方法,以便实例化长按手势识别器,设置其代表,使用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];
}

同样不要忘记实现使该手势失败并允许同时识别的UIGestureRecognizerDelegate方法(您可能会或可能不需要实现它,这取决于您拥有的其他手势识别器或内部手势识别器的依赖性)

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

    return NO;
}

凭据用于LXReorderableCollectionViewFlowLayout的内部实现


这与我为Snapchat克隆制作的舞蹈相同。啊,真爱。
benjaminhallock

1

斯威夫特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)")
    }
}

同样不要忘记实现UIGestureRecognizerDelegate并从viewDidLoad或需要调用它的地方调用setupLongGestureRecognizerOnCollection。


0

也许,使用UILongPressGestureRecognizer是最广泛的解决方案。但是我遇到了两个烦人的麻烦:

  • 有时,当我们移动触摸屏时,此识别器会以不正确的方式工作;
  • 识别器会拦截其他触摸操作,因此我们不能以适当的方式使用UICollectionView的突出显示回调。

让我提出一个蛮力的建议,但是要按照建议工作:

声明一个用于长时间单击单元格的回调描述:

typealias OnLongClickListener = (view: OurCellView) -> Void

用变量扩展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

在我们的单元格类中添加两个方法:

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

并在此处覆盖触摸事件:

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

然后在我们集合视图的控制器中的某处声明回调侦听器:

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

最后在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
}

现在我们可以拦截单元格上的长按动作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.