UITapGestureRecognizer-터치 업이 아닌 터치 다운에서 작동하도록 하시겠습니까?


84

탭 이벤트를 사용하는 것은 매우 시간에 민감하므로 사용자가 터치 업을 요구하는 대신 단순히 터치 다운 할 때 UITapGestureRecognizer를 활성화 할 수 있는지 궁금합니다.


1
도움이된다면 UITouch에는 touchesStarted 메소드가 있습니다. 그러나 그것은 당신이 요청한 것처럼 제스처 인식기를 사용하지 않습니다.
vqdave 2013 년

답변:


139

사용자 지정 TouchDownGestureRecognizer 하위 클래스를 만들고 touchesBegan에서 제스처를 구현합니다.

TouchDownGestureRecognizer.h

#import <UIKit/UIKit.h>

@interface TouchDownGestureRecognizer : UIGestureRecognizer

@end

TouchDownGestureRecognizer.m

#import "TouchDownGestureRecognizer.h"
#import <UIKit/UIGestureRecognizerSubclass.h>

@implementation TouchDownGestureRecognizer
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    if (self.state == UIGestureRecognizerStatePossible) {
        self.state = UIGestureRecognizerStateRecognized;
    }
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    self.state = UIGestureRecognizerStateFailed;
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    self.state = UIGestureRecognizerStateFailed;
}


@end

이행:

#import "TouchDownGestureRecognizer.h"
    TouchDownGestureRecognizer *touchDown = [[TouchDownGestureRecognizer alloc] initWithTarget:self action:@selector(handleTouchDown:)];
    [yourView addGestureRecognizer:touchDown];

-(void)handleTouchDown:(TouchDownGestureRecognizer *)touchDown{
    NSLog(@"Down");
}

신속한 구현 :

import UIKit
import UIKit.UIGestureRecognizerSubclass

class TouchDownGestureRecognizer: UIGestureRecognizer
{
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent)
    {
        if self.state == .Possible
        {
            self.state = .Recognized
        }
    }

    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent)
    {
        self.state = .Failed
    }

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent)
    {
        self.state = .Failed
    }
}

붙여 넣을 2017 년의 Swift 구문은 다음과 같습니다.

import UIKit.UIGestureRecognizerSubclass

class SingleTouchDownGestureRecognizer: UIGestureRecognizer {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        if self.state == .possible {
            self.state = .recognized
        }
    }
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
        self.state = .failed
    }
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
        self.state = .failed
    }
}

이것은 UITap. 그래서 코드에서 ...

func add(tap v:UIView, _ action:Selector) {
    let t = UITapGestureRecognizer(target: self, action: action)
    v.addGestureRecognizer(t)
}

안전하게 교체 할 수 있습니다 ....

func add(hairtriggerTap v:UIView, _ action:Selector) {
    let t = SingleTouchDownGestureRecognizer(target: self, action: action)
    v.addGestureRecognizer(t)
}

테스트 결과 두 번 이상 호출되지 않습니다. 드롭 인 대체품으로 작동합니다. 두 통화 사이를 전환 할 수 있습니다.


10
"UIGestureRecognizerSubclass.h"가져 오기에 대한 추가 +1. 좋은.
Sebastian Hojas 2013

3
touchesBegin, touchesMoved, touchesEnded 메서드에서 super를 호출하면 안 되나요?
neoneye 2014

3
당신은 잊지 @JasonSilberman # import를 <UIKit / UIGestureRecognizerSubclass.h>
LE는 SANG

이것으로 더블 탭을 어떻게 구현 하시겠습니까?
Babiker

1
@etayluz 당신은 시작과 끝 상태를 할당했습니다. 그런 다음 핸들의 상태를 확인하십시오. 사용자 지정 인식기는 모든 것을 제어 할 수 있음을 의미합니다.
LE SANG

184

UILongPressGestureRecognizer를 사용하고 minimumPressDuration0으로 설정합니다 UIGestureRecognizerStateBegan. 상태 동안 터치 다운처럼 작동합니다 .

Swift 4+ 용

func setupTap() {
    let touchDown = UILongPressGestureRecognizer(target:self, action: #selector(didTouchDown))
    touchDown.minimumPressDuration = 0
    view.addGestureRecognizer(touchDown)
}

@objc func didTouchDown(gesture: UILongPressGestureRecognizer) {
    if gesture.state == .began {
        doSomething()
    }
}

Objective-C 용

-(void)setupLongPress
{
   self.longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(didLongPress:)];
   self.longPress.minimumPressDuration = 0;
   [self.view addGestureRecognizer:self.longPress];
}

-(void)didLongPress:(UILongPressGestureRecognizer *)gesture
{
   if (gesture.state == UIGestureRecognizerStateBegan){
      [self doSomething];
   }
}

4
인터페이스 빌더 (나)를 좋아하는 사람들을 위해, minimumPressDuration은 IB에서도 설정할 수 있습니다 (훌륭한 솔루션에 대해 Rob에게 감사합니다)
tmr

여기에서 손질을 감지하는 방법이 있습니까?
CalZone 2014

1
예 @CalZone, 제스처 상태를 확인UIGestureRecognizerStateEnded
롭 캐러 웨이

2
좋은 해결 방법 +1. minimumPressDuration0도 될 수 있습니다.
Valentin Radu

1
위험 그것은 종종 전형적인 터치로 한 번 이상 호출 될 수 있습니다.
Fattie 2017

23

Swift (서브 클래 싱 없음)

다음은 Rob Caraway의 Objective-C 답변 과 유사한 Swift 버전 입니다.

아이디어는 minimumPressDuration탭 제스처 인식기를 사용하는 대신 0으로 설정된 길게 누르기 제스처 인식기를 사용하는 것입니다. 길게 누르기 제스처 인식기는 터치 시작 이벤트를보고하지만 탭 제스처는보고하지 않기 때문입니다.

import UIKit
class ViewController: UIViewController {

    @IBOutlet weak var myView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Add "long" press gesture recognizer
        let tap = UILongPressGestureRecognizer(target: self, action: #selector(tapHandler))
        tap.minimumPressDuration = 0
        myView.addGestureRecognizer(tap)
    }

    // called by gesture recognizer
    @objc func tapHandler(gesture: UITapGestureRecognizer) {

        // handle touch down and touch up events separately
        if gesture.state == .began {
            // do something...
            print("tap down")
        } else if gesture.state == .ended { // optional for touch up event catching
            // do something else...
            print("tap up")
        }
    }
}

1
@richy, 이름이 길게 누름 제스처 인식기 이기 때문에 나에게도 해킹처럼 느껴졌 지만이 방법은 뷰를 하위 클래스로 만드는 것보다 훨씬 쉽고 당신이 말한 것처럼 훌륭하게 작동합니다.
Suragch

이 솔루션은 UIscrollView 하위 클래스에서 스크롤 문제를 일으 킵니다
SPatel

나는 bater 방식이다 사용자 정의 제스처 인식기 생각
SPatel

@Suragch 나는 lessing의 대답으로 만난 것과 동일한 문제에 직면했습니다. 이 대답은 작동하지만 손가락을 문지르 기 위해 개체에 손가락을 대 자마자 터치라고 생각하기 때문에 문지르는 기능을 잃었습니다. 둘 다 어떻게 구별 할 수 있습니까?
Lance Samaria

1
@LanceSamaria, 탭하는 것보다 더 복잡한 터치 인식이 필요한 경우 사용자 지정 제스처 인식기를 사용합니다.
Suragch

1

이것은 또 다른 해결책입니다. UIControl의 하위 클래스를 만듭니다. UIControl은 UIView의 하위 클래스이기 때문에 Storyboard에서도 UIView처럼 사용할 수 있습니다.

class TouchHandlingView: UIControl {
}

그리고 그것에 addTarget :

@IBOutlet weak var mainView: TouchHandlingView!
...

mainView.addTarget(self, action: "startAction:", forControlEvents: .TouchDown)
...

그러면 지정된 작업이 UIButton과 같이 호출됩니다.

func startAction(sender: AnyObject) {
    print("start")
}

1

머리를 두 드리 자마자 반응 할 수 있도록 뷰에 헤어 트리거가있는 기능이 필요했습니다. 모두 사용 @LESANG 응답하는 일 등 사용했다 @RobCaraway의 대답을 . 답변 모두 문제 는 스 와이프를 인식하는 능력을 잃었다는 것입니다. 스 와이프 할 때 뷰를 회전해야했지만 손가락이 뷰에 닿 자마자 탭만 인식되었습니다. tapRecognizer가 너무 민감해서 탭과 스 와이프를 구분할 수 없습니다.

이것은 내가 기반 오프 해낸 것입니다 @LESANG 대답 이 결합 대답 .

각 이벤트에 6 개의 댓글을 달았습니다.

import UIKit.UIGestureRecognizerSubclass

class SingleTouchDownGestureRecognizer: UIGestureRecognizer {


    var wasSwiped = false

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {

        guard let view = self.view else { return }
        guard let touches = event.touches(for: view) else { return } // 1. compare that event in touchesBegan has touches for the view that is the same as the view to which your gesture recognizer was assigned

        if touches.first != nil {
            print("Finger touched!") // 2. this is when the user's finger first touches the view and is at locationA
            wasSwiped = false // 3. it would seem that I didn't have to set this to false because the property was already set to false but for some reason when I didn't add this it wasn't responding correctly. Basically set this to false
        }
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {

        guard let touch = touches.first else { return }

        let newLocation = touch.location(in: self.view)
        let previousLocation = touch.previousLocation(in: self.view)

        if (newLocation.x > previousLocation.x) || (newLocation.x < previousLocation.x) {
            print("finger touch went right or left") // 4. when the user's finger first touches it's at locationA. If the the user moves their finger to either the left or the right then the finger is no longer at locationA. That means it moved which means a swipe occurred so set the "wasSwiped" property to true

            wasSwiped = true // 5. set the property to true because the user moved their finger
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
        print("finger is no longer touching.") // 6. the user has lifted their finger off of the view. If "wasSwiped" is true then ".fail" but if it wasn't swiped then ".recognize"

        if wasSwiped {
            self.state = .failed
        } else {
            self.state = .recognized
        }
    }
}

그리고 그것을 사용하는 뷰가 헤어 트리거 응답과 왼쪽 및 오른쪽 스 와이프 제스처를 얻도록 사용하려면 :

let tapGesture = SingleTouchDownGestureRecognizer(target: self, action: #selector(viewWasTapped(_:)))
myView.addGestureRecognizer(tapGesture)

let rightGesture = UISwipeGestureRecognizer(target: self, action: #selector(respondToSwipeGesture(recognizer:)))
rightGesture.direction = .right
myView.addGestureRecognizer(rightGesture)

let leftGesture = UISwipeGestureRecognizer(target: self, action: #selector(respondToSwipeGesture(recognizer:)))
leftGesture.direction = .left
myView.addGestureRecognizer(leftGesture)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.