UITapGestureRecognizer nhấn vào self.view nhưng bỏ qua các lượt xem phụ


95

Tôi cần triển khai một tính năng sẽ gọi một số mã khi tôi nhấn đúp vào self.view (chế độ xem của UIViewCotroller). Nhưng vấn đề là tôi có đối tượng giao diện người dùng khác trên chế độ xem này và tôi không muốn đính kèm bất kỳ đối tượng nhận dạng nào vào tất cả chúng. Tôi đã tìm thấy phương pháp này dưới đây để thực hiện cử chỉ trên chế độ xem của mình và tôi biết cách hoạt động của nó. Ngay bây giờ tôi đang đứng trước handicap nên chọn cách nào để tạo trình nhận dạng này bỏ qua lượt xem phụ. Bất kỳ ý tưởng? Cảm ơn.

UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
[doubleTap setNumberOfTapsRequired:2];
[self.view addGestureRecognizer:doubleTap];

1
Tôi không chắc, nhưng bạn đã thử đặt cancelsTouchesInView thành KHÔNG trên trình nhận dạng chưa? so [doubleTap setCancelsTouchesInView: NO];
JDx

Câu trả lời:


144

Bạn nên sử dụng UIGestureRecognizerDelegategiao thức bên trong selfđối tượng và gọi phương thức bên dưới để kiểm tra chế độ xem. Bên trong phương pháp này, hãy kiểm tra chế độ xem của bạn touch.viewvà trả về bool thích hợp (Có / Không). Một cái gì đó như thế này:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([touch.view isDescendantOfView:yourSubView]) {
        return NO;
    }
    return YES;
}

Chỉnh sửa: Vui lòng kiểm tra câu trả lời của @ Ian!

Swift 5

// MARK: UIGestureRecognizerDelegate methods, You need to set the delegate of the recognizer
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
     if touch.view?.isDescendant(of: tableView) == true {
        return false
     }
     return true
}

một câu hỏi nữa Tôi có một số nút trên chế độ xem của tôi phải hoạt động. làm thế nào tôi có thể đặt một số ưu tiên cho chúng?
Matrosov Alexander

nếu các nút của bạn có recognizers cử chỉ của mình (khả năng), thâm nhập vào internals của họ và lấy họ, và đọc các tài liệu trên -[UIGestureRecognizer requireGestureRecognizerToFail:]nhưng nó có thể làm việc mà không mod'ing họ
bshirley

đã tìm kiếm điều này trong nhiều giờ! Cảm ơn! ;)
MasterRazer

Nếu ifthử nghiệm của bạn không thành công, việc triển khai của bạn không trả lại BOOL; để trả về kiểu thích hợp CÓ sau một khối if (using { }) hoặc trong một elsenhánh. Cảm ơn, mặc dù, đã tiết kiệm cho tôi một chút đọc.
RobP

2
Một quan điểm là một hậu duệ của bản thân vì vậy đây không làm việc ... (cách tiếp cận tổng thể được mặc dù ok, xem câu trả lời khác)
Ixx

107

Một cách tiếp cận khác là chỉ so sánh xem chế độ xem của cảm ứng có phải là chế độ xem cử chỉ hay không, bởi vì các con cháu sẽ không vượt qua điều kiện. Một lớp lót đơn giản, đẹp:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return touch.view == gestureRecognizer.view
}

2
Điều này đang làm việc tốt hơn cho tôi so với câu trả lời được chấp nhận. Ngắn gọn hơn nhiều.
Suman Roy

1
@Ian phương thức này không được gọi khi nhấn vào dạng xem.
Praburaj 14/09/17

1
Câu trả lời ngắn gọn tuyệt vời!
scurioni

Câu trả lời được chấp nhận thậm chí không hoạt động nhưng điều này đã hoạt động hoàn hảo đối với tôi.
Shalin Shah

@Prabu propably vì đại biểu cử chỉ nhận dạng phải được thiết lập trước
Kubba

23

Và đối với biến thể Swift:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if touch.view.isDescendantOfView(yourSubView){
        return false
    }
    return true
}

Điều cần biết là isDescendantOfViewtrả về một Booleangiá trị cho biết người nhận là một chế độ xem phụ của một chế độ xem nhất định hay giống hệt với chế độ xem đó.


1
Đừng quên đặt UIGestureRecognizerDelegate trong lớp của bạn!
Fox5150,

4
Thay vì làm điều đó nếu câu lệnh, bạn không thể chỉ trả lại điều này? return! touch.view.isDescendant (của: scrollRecognizer.view) Ngoài ra, cú pháp mới trong swift 3 ^^
EdwardSanchez

12

Với Swift 5 và iOS 12, UIGestureRecognizerDelegatecó một phương thức được gọi gestureRecognizer(_:shouldReceive:). gestureRecognizer(_:shouldReceive:)có khai báo sau:

Hỏi đại biểu xem liệu trình nhận dạng cử chỉ có nhận được một đối tượng đại diện cho một lần chạm hay không.

optional func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool

Đoạn mã hoàn chỉnh dưới đây cho thấy một triển khai có thể có cho gestureRecognizer(_:shouldReceive:). Với mã này, một lần nhấn vào ViewControllerchế độ xem phụ của chế độ xem của (bao gồm imageView) sẽ không kích hoạt printHello(_:)phương pháp.

import UIKit

class ViewController: UIViewController, UIGestureRecognizerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(printHello))
        tapGestureRecognizer.delegate = self
        view.addGestureRecognizer(tapGestureRecognizer)

        let imageView = UIImageView(image: UIImage(named: "icon")!)
        imageView.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
        view.addSubview(imageView)

        // ⚠️ Enable user interaction for imageView so that it can participate to touch events.
        // Otherwise, taps on imageView will be forwarded to its superview and managed by it.
        imageView.isUserInteractionEnabled = true
    }

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        // Prevent subviews of a specific view to send touch events to the view's gesture recognizers.
        if let touchedView = touch.view, let gestureView = gestureRecognizer.view, touchedView.isDescendant(of: gestureView), touchedView !== gestureView {
            return false
        }
        return true
    }

    @objc func printHello(_ sender: UITapGestureRecognizer) {
        print("Hello")
    }

}

Một cách triển khai thay thế gestureRecognizer(_:shouldReceive:)có thể là:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return gestureRecognizer.view === touch.view
}

Tuy nhiên, lưu ý rằng mã thay thế này không kiểm tra xem có phải touch.viewlà một lượt xem phụ của gestureRecognizer.view.


10

Giải pháp nhanh chóng hoàn chỉnh (phải triển khai ủy quyền VÀ được đặt cho (các) trình nhận dạng):

class MyViewController: UIViewController UIGestureRecognizerDelegate {

    override func viewDidLoad() {
        let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(onBaseTapOnly))
        doubleTapRecognizer.numberOfTapsRequired = 2
        doubleTapRecognizer.delegate = self
        self.view.addGestureRecognizer(doubleTapRecognizer)
    }

    func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
        if touch.view.isDescendantOfView(self.view){
            return false
        }
        return true
    }

    func onBaseTapOnly(sender: UITapGestureRecognizer) {
        if sender.state == .Ended {
            //react to tap
        }
    }
}

6

Biến thể sử dụng CGPoint bạn chạm vào (SWIFT 4.0)

class MyViewController: UIViewController, UIGestureRecognizerDelegate {

  func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {

// Get the location in CGPoint
    let location = touch.location(in: nil)

// Check if location is inside the view to avoid
    if viewToAvoid.frame.contains(location) {
        return false
    }

    return true
  }
}

4

Rõ ràng cách Swift

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return touch.view == self.view
}

Không bao giờ được gọi trong iOS 13, ít nhất là với VuốtGestureRecognizer.
Chewie The Chorkie 19/09/19

Điều này khác với câu trả lời của Ian như thế nào?
sweetfa

3

Lưu ý rằng API cử chỉ cử chỉ đã thay đổi thành:

movingRecognizer (_: shouldReceive :)

Đặc biệt lưu ý về chỉ báo gạch dưới (bỏ qua) cho nhãn ngoài của thông số đầu tiên.

Sử dụng nhiều ví dụ được cung cấp ở trên, tôi đã không nhận được sự kiện. Dưới đây là một ví dụ hoạt động cho các phiên bản hiện tại của Swift (3+).

public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    var shouldReceive = false
    if let clickedView = touch.view {
        if clickedView == self.view {
            shouldReceive = true;
        }
    }
    return shouldReceive
}

2

Ngoài các giải pháp trên, đừng quên kiểm tra xem User Interaction Enabledphụ của bạn.

nhập mô tả hình ảnh ở đây


2

Tôi đã phải ngăn cử chỉ trên chế độ xem trẻ em. Điều duy nhất hoạt động là cho phép và giữ chế độ xem đầu tiên và ngăn cử chỉ trong tất cả các chế độ xem tiếp theo:

   var gestureView: UIView? = nil

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if (gestureView == nil || gestureView == touch.view){
            gestureView = touch.view
            return true
        }
        return false
     }

1

Swift 4:

touch.view hiện là tùy chọn, vì vậy dựa trên câu trả lời của @ Antoine:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if let touchedView = touch.view, touchedView.isDescendant(of: deductibleBackgroundView) {
        return false
    }
    return true
}

0

Nếu bạn không muốn 'nhấp đúp recogniser' của bạn xung đột với các nút và / hoặc điều khiển khác, bạn có thể thiết lập selfnhư UIGestureRecognizerDelegatevà thực hiện:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
{
    return !(touch.view is UIControl)
}
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.