UITapGestureRecognizer - làm cho nó hoạt động khi chạm xuống chứ không phải chạm lên?


84

Những gì tôi đang sử dụng sự kiện chạm rất nhạy cảm về thời gian, vì vậy tôi tò mò liệu có thể kích hoạt UITapGestureRecognizer khi người dùng chỉ cần chạm xuống, thay vì yêu cầu họ chạm vào không?


1
Nếu hữu ích, UITouch có một phương thức TouchStarted. Nhưng đó không phải là sử dụng công cụ nhận dạng cử chỉ, như bạn đã hỏi.
vqdave

Câu trả lời:


139

Tạo lớp con TouchDownGestureRecognizer tùy chỉnh của bạn và triển khai cử chỉ khi chạm vàoBegan:

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

thực hiện:

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

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

Triển khai nhanh chóng:

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

Đây là cú pháp Swift cho năm 2017 để dán:

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

Lưu ý rằng đây là một thay thế thả vào cho UITap. Vì vậy, trong mã như ...

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

bạn có thể hoán đổi một cách an toàn sang ....

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

Thử nghiệm cho thấy nó sẽ không được gọi nhiều hơn một lần. Nó hoạt động như một sự thay thế thả vào; bạn chỉ có thể hoán đổi giữa hai cuộc gọi.


10
+1 cho lần nhập "UIGestureRecognizerSubclass.h". Đẹp.
Sebastian Hojas

3
Không nên gọi super trong các phương thức touchBegin, touchMoved, touchEnded?
neoneye

3
@JasonSilberman bạn quên #import <UIKit / UIGestureRecognizerSubclass.h>
LÊ SANG

Làm thế nào bạn sẽ thực hiện nhấn đúp với điều này?
Babiker

1
@etayluz bạn đã chỉ định trạng thái bắt đầu và kết thúc. Sau đó, kiểm tra trạng thái của nó trong xử lý. Trình nhận dạng tùy chỉnh có nghĩa là bạn có thể kiểm soát mọi thứ.
LÊ SANG

184

Sử dụng UILongPressGestureRecognizer và đặt nó minimumPressDurationthành 0. Nó sẽ hoạt động giống như một lần chạm xuống trong UIGestureRecognizerStateBegantrạng thái.

Đối với 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()
    }
}

Đối với 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
đối với những người thích trình tạo giao diện (tôi), tối thiểuPressDuration cũng có thể được đặt trong IB (cảm ơn bạn Rob về giải pháp tuyệt vời)
tmr

Có cách nào để phát hiện liên lạc ở đây không?
CalZone

1
@CalZone Vâng, kiểm tra trạng thái cử chỉUIGestureRecognizerStateEnded
Rob Caraway

2
Một cách giải quyết tốt +1. minimumPressDurationcó thể là 0 tho.
Valentin Radu

1
NGUY HIỂM nó thường có thể được gọi nhiều lần, với một lần chạm điển hình.
Fattie

23

Swift (không có phân lớp)

Đây là một phiên bản Swift tương tự như câu trả lời Objective-C của Rob Caraway .

Ý tưởng là sử dụng trình nhận dạng cử chỉ nhấn và giữ minimumPressDurationđược đặt thành 0 thay vì sử dụng trình nhận dạng cử chỉ nhấn. Điều này là do trình nhận dạng cử chỉ nhấn và giữ báo cáo các sự kiện chạm đã bắt đầu trong khi cử chỉ nhấn thì không.

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, Nó cảm thấy một chút giống như một hack với tôi nữa, vì tên của nó là dài báo chí cử chỉ nhận dạng, nhưng phương pháp này là dễ dàng hơn rất nhiều so với subclassing quan điểm và như bạn nói, nó hoạt động tuyệt vời.
Suragch

Giải pháp này gây ra vấn đề di chuyển trong lớp con UIScrollView
SPatel

Tôi nghĩ rằng cách tiếp cận bater là Tuỳ chỉnh Gesture recognizer
SPatel

@Suragch Tôi gặp phải vấn đề tương tự với vấn đề này mà tôi đã gặp phải với câu trả lời ít hơn. Câu trả lời này hoạt động nhưng tôi đã mất khả năng vuốt vì ngay sau khi tôi đặt ngón tay lên đối tượng để vuốt, nó nghĩ rằng đó là một cú chạm. Làm thế nào nó có thể phân biệt giữa cả hai?
Lance Samaria

1
@LanceSamaria, Nếu bạn cần nhận dạng cảm ứng phức tạp hơn là chỉ nhấn, tôi sẽ sử dụng công cụ nhận dạng cử chỉ tùy chỉnh.
Suragch

1

Đây là một giải pháp khác. Tạo lớp con của UIControl. Bạn có thể sử dụng nó như UIView ngay cả trong Storyboard vì UIControl là lớp con của UIView.

class TouchHandlingView: UIControl {
}

Và addTarget vào nó:

@IBOutlet weak var mainView: TouchHandlingView!
...

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

Sau đó, hành động được chỉ định sẽ được gọi như UIButton:

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

1

Tôi cần khả năng kích hoạt tóc để chế độ xem của tôi có thể phản hồi ngay khi chạm vào. Sử dụng cả câu trả lời @LESANG đều hiệu quả và sử dụng câu trả lời @RobCaraway cũng vậy . Vấn đề tôi gặp phải với cả hai câu trả lời là tôi mất khả năng nhận dạng các lần vuốt. Tôi cần chế độ xem của mình xoay khi được vuốt nhưng ngay khi ngón tay tôi chạm vào chế độ xem, chỉ có vòi được nhận dạng. TapRecognizer quá nhạy cảm và không thể phân biệt giữa một lần nhấn và một lần vuốt.

Đây là những gì tôi nghĩ ra dựa trên câu trả lời @LESANG kết hợp với câu trả lời này và câu trả lời này .

Tôi đặt 6 bình luận trong mỗi sự kiện.

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

Và để sử dụng nó để chế độ xem sử dụng nó sẽ nhận được phản hồi kích hoạt tóc và các cử chỉ vuốt sang trái và phải:

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.