UICollectionViewCellでの長押しジェスチャー


108

長押しジェスチャー認識機能をUICollectionView(のサブクラス)に追加する方法を知りたいと思っていました。私はそれがデフォルトで追加されることをドキュメントで読みましたが、私は方法を理解することができません。

私がやりたいことは、セルを長押しします(私はgithubからカレンダーを用意しています)。どの細胞が長押しされているかを知る必要があります。この幅広い質問でごめんなさい、しかし私はグーグルでもSOでももっと良いものを見つけることができませんでした

回答:


220

Objective-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
abbood

10
(少なくとも)ios7の場合lpgr.delaysTouchesBegan = YES;didHighlightItemAtIndexPath最初にトリガーされるのを避けるために追加する必要があります。
DynamicDan 2014年

7
なぜ追加したのlpgr.delegate = self;ですか?あなたが提供しなかったデリゲートなしでもうまく動作します。
Yevhen Dubinin 2014

3
@abbood答えは機能しますが、長押し認識機能がアクティブな間、(別の指を使用して)コレクションビューを上下にスクロールできません。何ができますか?
PéturIngi Egilsson

4
個人的にはそうUIGestureRecognizerStateBeganするので、ジェスチャーはユーザーが指を離したときではなく、認識されたときに使用されます。
Jeffrey Sun、

28

同じコード@abboodの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


3
素晴らしい仕事、ちょうどノート「handleLongPress:」その#selectorに変更する必要があります(YourViewController.handleLongPress(_ :))
ジョセフGeraghty

16
うまく機能しますが、ユーザーが指を離したときだけでなく、最小期間が経過したときにコードを実行UIGestureRecognizerState.EndedするUIGestureRecognizerState.Beganように変更します。
Crashalot 2016年

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もキックオフされます。これは、長押しと単押しの2つの異なるアクションが必要なときに問題になりました。
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は要求していませんその種の行動についても、彼がそれを置き換えようとしているのでもない
13

防御的に聞こえて申し訳ありませんが、私はiOSアプリで上記のコードを数か月間使用してきました。グリッチが発生したことを1回考えることはできません
13:12

@abbood結構です。POが使用しているサードパーティのカレンダーコンポーネントはわかりませんが、発生する可能性はないと考えてもグリッチを防ぐことは悪い考えではありません;-)
tiguero

デフォルトの認識機能が失敗するのを待つ必要がある場合、それは遅延があることを意味しませんか?
Crashalot 2016年

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 2016

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を使用することが最も普及しているソリューションです。しかし、私は2つの厄介な問題に遭遇しました。

  • タッチを動かしているときに、この認識機能が正しく動作しないことがあります。
  • レコグナイザは他のタッチアクションをインターセプトするため、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

セルクラスに2つのメソッドを追加します。

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

これで、セルのロングクリックアクションをインターセプトできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.