UICollectionViewCell 上的长按手势

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

我想做的是: Long press on a cell ( 我有个 Github 的日历 ), get which cell is tapped and then do stuff with it. 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

95558 次浏览

目标 C

In your myCollectionViewController.h file add the UIGestureRecognizerDelegate protocol

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

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

根据文档 给你,在这里添加自定义长按手势识别器的答案是正确的 然而: 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];
}

同样的代码@abbod 对 Swift 的代码:

在 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

要在 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

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

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

另外,不要忘记实现 UIGestureIdentiizerGenerate 方法,这些方法会导致手势识别失败并允许同步识别(你可能需要也可能不需要实现它,它取决于你拥有的其他手势识别器或者与内部手势识别器的依赖关系)

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

使用 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
}

也许,使用 手势识别器是最普遍的解决方案,但我遇到了两个恼人的问题:

  • 有时当我们移动触摸时,这个识别器的工作方式不正确;
  • 识别程序拦截其他触摸动作,因此我们不能以适当的方式使用 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):

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

在 cell 类中添加两个方法:

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

现在我们可以拦截我们手机上的长点击动作了。

迅捷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)")
}
}

另外,不要忘记实现 UIGestureIdentiizer 委托,并从 viewDidLoad 或任何需要调用它的地方调用 setupLongGestureIdentiizerOnCollection。

更简单的解决办法。

In your 细胞对物质 delegate (set .tag property for later):

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

返回 longPress:

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