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 2014-08-07

3
@abbood 대답이 작동하지만 길게 누르기 인식기가 활성화되어있는 동안 다른 손가락을 사용하여 collectionview에서 위아래로 스크롤 할 수 없습니다. 무엇을 제공합니까?
Pétur Ingi 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
#selector (YourViewController.handleLongPress (_ :))로 변경해야 "handleLongPress는"위대한 단지 참고 일했다
조셉 라그 티

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에 대해 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는 묻지 않습니다. 그런 종류의 행동에 대해 그는 그것을 대체하려고하지 않습니다
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를 서브 클래 싱 한 경우 가장 좋은 옵션은 서브 클래스에 있습니다).

@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 클론을 위해했던 것과 동일한 춤입니다. ahhh 진정한 사랑.
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
}

이제 셀에서 긴 클릭 동작을 가로 챌 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.