ท่าทางกดแบบยาวบน UICollectionViewCell


108

ฉันสงสัยว่าจะเพิ่มเครื่องมือจดจำท่าทางการกดแบบยาวให้กับ UICollectionView (คลาสย่อยของ) ได้อย่างไร ฉันอ่านในเอกสารว่ามีการเพิ่มโดยค่าเริ่มต้น แต่ฉันคิดไม่ออกว่าจะทำอย่างไร

สิ่งที่ฉันต้องการทำคือ: กดที่เซลล์ค้างไว้ ( ฉันมีปฏิทินจาก github ) รับเซลล์ที่แตะแล้วทำสิ่งต่างๆกับมัน ฉันต้องการทราบว่าเซลล์ใดถูกบีบอัดมานาน ขออภัยสำหรับคำถามกว้าง ๆ นี้ แต่ฉันไม่พบสิ่งที่ดีกว่าทั้งใน Google หรือ SO

คำตอบ:


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
abbood

10
สำหรับ (อย่างน้อย) ios7 คุณต้องเพิ่มlpgr.delaysTouchesBegan = YES;เพื่อหลีกเลี่ยงการdidHighlightItemAtIndexPathถูกทริกเกอร์ก่อน
DynamicDan

7
ทำไมคุณถึงเพิ่มlpgr.delegate = self;? ทำงานได้ดีโดยไม่ต้องมีผู้รับมอบสิทธิ์ซึ่งคุณไม่ได้ให้ไว้ด้วย
Yevhen Dubinin

3
@abbood คำตอบใช้งานได้ แต่ฉันไม่สามารถเลื่อนขึ้นและลงในมุมมองคอลเลกชัน (โดยใช้นิ้วอื่น) ในขณะที่ตัวจดจำการกดแบบยาวทำงานอยู่ สิ่งที่ช่วยให้?
Pétur Ingi 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 (_ :))
Joseph Geraghty

16
ใช้งานได้ดี แต่UIGestureRecognizerState.Endedให้เปลี่ยนเป็นUIGestureRecognizerState.Beganถ้าคุณต้องการให้โค้ดเริ่มทำงานเมื่อผ่านระยะเวลาขั้นต่ำไปแล้วไม่ใช่เฉพาะเมื่อผู้ใช้ยกนิ้วขึ้น
Crashalot

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 ก็จะมีการยกเลิก didSelectItemAtIndexPath ด้วยเช่นกัน สิ่งนี้กลายเป็นปัญหาสำหรับฉันเมื่อฉันต้องการสองการกระทำที่แตกต่างกันสำหรับการกดแบบยาวและการกดครั้งเดียว
ShannonS

ตอนนี้เป็นวิธีที่เลิกใช้แล้วคุณสามารถใช้ - (UIContextMenuConfiguration *) collectionView: (UICollectionView *) collectionView contextMenuConfigurationForItemAtIndexPath: (nonnull NSIndexPath *) indexPath point: (CGPoint) point;
Viktor Goltvyanitsa

8

คำตอบที่นี่เพื่อเพิ่มเครื่องมือจดจำท่าทางสัมผัสแบบ longpress ที่กำหนดเองนั้นถูกต้องอย่างไรก็ตามตามเอกสารประกอบที่นี่ : คลาสพาเรนต์ของ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 ไม่ได้ถาม เกี่ยวกับพฤติกรรมแบบนั้นและเขาไม่ได้พยายามที่จะแทนที่มัน
abbood

ขอโทษที่ทำให้เกิดการป้องกัน แต่ฉันใช้รหัสด้านบนกับแอป iOS มาหลายเดือนแล้ว .. นึกไม่ออกเลยว่าจะเกิดความผิดพลาดสักครั้งเดียว
Abbood

@abbood ไม่เป็นไร ฉันไม่รู้ส่วนประกอบปฏิทินของบุคคลที่สามที่ PO ใช้ แต่ฉันคิดว่าการป้องกันความผิดพลาดแม้ว่าคุณจะคิดว่ามันไม่มีทางเกิดขึ้นได้ก็ไม่ใช่ความคิดที่ไม่ดี ;-)
tiguero

หากคุณต้องรอให้ตัวจดจำเริ่มต้นล้มเหลวไม่ได้หมายความว่าจะมีความล่าช้า?
Crashalot

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 แบบ subclass)

@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 และเรียก setupLongGestureRecognizerOnCollection จาก viewDidLoad หรือที่ใดก็ตามที่คุณต้องการเรียกใช้


0

บางทีการใช้UILongPressGestureRecognizerเป็นวิธีแก้ปัญหาที่แพร่หลายที่สุด แต่ฉันพบกับปัญหาที่น่ารำคาญสองประการ:

  • บางครั้งเครื่องมือจดจำนี้ทำงานผิดวิธีเมื่อเราขยับสัมผัส
  • โปรแกรมจดจำจะสกัดกั้นการสัมผัสอื่น ๆ ดังนั้นเราจึงไม่สามารถใช้ไฮไลต์เรียกกลับของ UICollectionView ของเราได้อย่างเหมาะสม

ให้ฉันแนะนำ bruteforce สักหน่อย แต่ทำงานตามที่ต้องการคำแนะนำ:

การประกาศคำอธิบายการโทรกลับสำหรับการคลิกที่เซลล์ของเราเป็นเวลานาน:

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
}

ตอนนี้เราสามารถสกัดกั้นการคลิกแบบยาวบนเซลล์ของเราได้แล้ว

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.