UICollectionViewCell üzerinde uzun basın hareketi


108

Bir (alt sınıf) UICollectionView'e uzun basın hareketi tanıyıcıyı nasıl ekleyeceğimi merak ediyordum. Belgelerde varsayılan olarak eklendiğini okudum, ancak nasıl olduğunu anlayamıyorum.

Yapmak istediğim şey: Bir hücreye uzun basmak ( github'dan bir takvim şeyim var ), hangi hücreye dokunulduğunu öğrenin ve sonra onunla bir şeyler yapın. Hangi hücrenin uzun süre basılı olduğunu bilmem gerekiyor. Bu kapsamlı soru için üzgünüm ama ne google ne de SO'da daha iyi bir şey bulamadım

Yanıtlar:


220

Amaç-C

Sizin de myCollectionViewController.hdosyaya eklemek UIGestureRecognizerDelegateprotokolü

@interface myCollectionViewController : UICollectionViewController<UIGestureRecognizerDelegate>

sizin de myCollectionViewController.mdosyaya:

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

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
zaten cevapta: UICollectionViewCell* cell = [self.collectionView cellForItemAtIndexPath:indexPath];referans burada tüm bunların doğru bir cevabı hak ettiğini umuyoruz: D
abbood

10
(En azından) ios7 lpgr.delaysTouchesBegan = YES;için didHighlightItemAtIndexPathönce tetiklenmeyi önlemek için eklemeniz gerekir .
DynamicDan

7
Neden ekledin lpgr.delegate = self;? Sizin de sağlamadığınız temsilci olmadan sorunsuz çalışır.
Yevhen Dubinin

3
@abbood yanıt çalışıyor, ancak uzun basma tanıyıcı etkinken koleksiyon görünümünde (başka bir parmak kullanarak) yukarı ve aşağı kaydıramıyorum. Ne oluyor?
Pétur Ingi Egilsson

4
Şahsen ben yaparım UIGestureRecognizerStateBegan, bu yüzden hareket, kullanıcı parmağını bıraktığında değil, tanındığında kullanılır.
Jeffrey Sun

28

Aynı kod @ abbood'un Swift için kodu:

ViewDidLoad'da:

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

Ve işlev:

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
    }

}

Temsilciyi unutma UIGestureRecognizerDelegate


3
Harika çalıştı, sadece "handleLongPress:" in #selector (YourViewController.handleLongPress (_ :)) olarak değiştirilmesi gerektiğine dikkat edin
Joseph Geraghty

16
İşleri iyi, ama değişim UIGestureRecognizerState.Endediçin UIGestureRecognizerState.Beganminimum süre geçtikten sonra sen, ateşe kod isterseniz sadece zaman onun / onun parmak yukarı kullanıcı seçer.
Crashalot

11

UICollectionView temsilcisini kullanarak uzun basın olayını alın

Aşağıdaki 3 yöntemi uygulamalısınız.

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

Not: shouldShowMenuForItemAtIndexPath için false değerini döndürürseniz, didSelectItemAtIndexPath de başlatılacaktır. Uzun basış ve tek basış için iki farklı eylem istediğimde bu benim için sorun oldu.
ShannonS

şimdilik kullanımdan kaldırılmış yöntemler, - (UIContextMenuConfiguration *) collectionView: (UICollectionView *) collectionView contextMenuConfigurationForItemAtIndexPath: (nonnull NSIndexPath *) indexPath noktası: (CGPoint) noktası;
Viktor Goltvyanitsa

8

Burada Cevapları doğru tanıyıcı özel longpress jest eklemek için ancak belgelerine göre burada : ana sınıfı UICollectionViewsınıfına bir yüklerdefault long-press gesture recognizer size toplama görüntüsü ilişkilendirilen varsayılan tanıyıcıya özel dokunma hareketi tanıyıcı bağlamalıdır böylece kaydırma etkileşimleri işlemek için.

Aşağıdaki kod, özel hareket tanıyıcınızın varsayılan olanı engellemesini önleyecektir:

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

Ne dediğini anlıyorum, ancak bu siyah beyaz değil, dokümantasyon şöyle diyor: 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.bu yüzden varsayılan uzun basma tanıyıcı kaydırma için yapılmıştır .. bu da dikey bir hareketle birlikte olması gerektiği anlamına gelir .. OP sormuyor bu tür davranışlar hakkında ne de onu değiştirmeye çalışıyor
abbood

savunma amaçlı
göründüğüm

@abbood bu iyi. PO'nun kullandığı üçüncü taraf takvim bileşenini bilmiyorum, ancak bunun asla olmayacağını düşünseniz bile bir aksaklığı önlemenin kötü bir fikir olmadığını düşünüyorum ;-)
tiguero

Varsayılan tanıyıcının başarısız olmasını beklemeniz gerekiyorsa, bu bir gecikme olacağı anlamına gelmez mi?
Crashalot

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

[cell addGestureRecognizer:longPress];

ve buna benzer bir yöntem ekleyin.

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

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

2

Harici bir hareket tanıyıcıya sahip olmak ve UICollectionView'daki dahili hareket tanıyıcılarla çakışmamak için şunları yapmanız gerekir:

Hareket tanıyıcınızı ekleyin, ayarlayın ve bir yerde onun için bir referans yakalayın (UICollectionView alt sınıfını seçtiyseniz en iyi seçenek alt sınıfınızdadır)

@interface UICollectionViewSubclass : UICollectionView <UIGestureRecognizerDelegate>    

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

@end

Sebep olduğu varsayılan başlatma yöntemleri initWithFrame:collectionViewLayout:ve initWithCoder:ve uzun basıldığında jest tanıyıcı yöntemi kurmak ekleyin

@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

Kurulum yönteminizi yazın, uzun basma hareketi tanıyıcıyı başlatacak, temsilcisini ayarlayacak, UICollectionView hareket tanıyıcıyla bağımlılıkları ayarlayacak (böylece ana hareket olacak ve diğer tüm hareketler tanınmadan önce bu hareket başarısız olana kadar bekleyecektir) ve görünüme hareket ekleyin

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

Ayrıca, bu hareketi başarısız olan ve eşzamanlı tanımaya izin veren UIGestureRecognizerDelegate yöntemlerini uygulamayı unutmayın (bunu uygulamanız gerekebilir veya gerekmeyebilir, sahip olduğunuz diğer hareket tanıyıcılara veya dahili hareket tanıyıcılarla bağımlılıklara bağlıdır)

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

    return NO;
}

bunun için kimlik bilgileri LXReorderableCollectionViewFlowLayout'un dahili uygulamasına gider


Bu, Snapchat klonum için yaptığım dansın aynısı. ahhh gerçek aşk.
benjaminhallock

1

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

Ayrıca, UIGestureRecognizerDelegate'i uygulamayı ve viewDidLoad'dan veya onu çağırmanız gereken her yerden setupLongGestureRecognizerOnCollection'ı çağırmayı unutmayın.


0

Belki de UILongPressGestureRecognizer'ı kullanmak en yaygın çözümdür. Ama bununla iki can sıkıcı sorunla karşılaşıyorum:

  • bazen bu tanıyıcı, dokunuşumuzu hareket ettirdiğimizde yanlış şekilde çalışır;
  • tanıyıcı, diğer dokunma eylemlerini engeller, bu nedenle UICollectionView'umuzun vurgulama geri aramalarını uygun bir şekilde kullanamayız.

Biraz kaba kuvvet önermeme izin verin, ancak gerektiği gibi çalışarak öneri:

Hücreye uzun tıklama için bir geri arama açıklaması bildirmek:

typealias OnLongClickListener = (view: OurCellView) -> Void

UICollectionViewCell'i değişkenlerle genişletme (örneğin, OurCellView olarak adlandırabiliriz):

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

Hücre sınıfımıza iki yöntem eklemek:

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

Ve burada dokunma olaylarını geçersiz kılıyor:

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

Ardından, geri arama dinleyicisini bildiren koleksiyon görünümümüzün denetleyicisinde bir yerde:

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

Ve son olarak, cellForItemAtIndexPath , hücrelerimiz için geri arama ayarında:

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

Artık hücrelerimizdeki uzun tıklama eylemlerini durdurabiliriz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.