UITapGestureRecognizer แตะที่ self.view แต่ไม่สนใจมุมมองย่อย


97

ฉันจำเป็นต้องใช้คุณลักษณะที่จะเรียกใช้โค้ดบางอย่างเมื่อฉันแตะสองครั้งที่ self.view (มุมมองUIViewCotroller) แต่ปัญหาที่ฉันมีออบเจ็กต์ UI อื่นในมุมมองนี้และฉันไม่ต้องการแนบอ็อบเจ็กต์จดจำใด ๆ กับพวกเขาทั้งหมด ฉันพบวิธีนี้ด้านล่างวิธีแสดงท่าทางในมุมมองของฉันและฉันรู้ว่ามันทำงานอย่างไร ตอนนี้ฉันอยู่หน้าแฮนดิแคปว่าจะเลือกวิธีใดในการสร้างตัวจดจำนี้โดยไม่สนใจมุมมองย่อย ความคิดใด ๆ ? ขอบคุณ.

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

1
ฉันไม่แน่ใจ แต่คุณได้ลองตั้งค่า cancelsTouchesInView เป็น NO บนโปรแกรมจดจำหรือไม่ ดังนั้น [doubleTap setCancelsTouchesInView: NO];
JDx

คำตอบ:


147

คุณควรใช้UIGestureRecognizerDelegateโปรโตคอลภายในselfวัตถุและเรียกใช้วิธีการด้านล่างเพื่อตรวจสอบมุมมอง ในวิธีนี้ตรวจสอบมุมมองของคุณtouch.viewและส่งคืนบูลที่เหมาะสม (ใช่ / ไม่ใช่) สิ่งนี้:

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

แก้ไข: โปรดตรวจสอบคำตอบของ @ Ian ด้วย!

สวิฟต์ 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
}

อีกคำถามหนึ่งฉันมีปุ่มไม่กี่ปุ่มในมุมมองของฉันที่ต้องใช้งานได้ ฉันจะกำหนดลำดับความสำคัญให้กับพวกเขาได้อย่างไร
Matrosov Alexander

หากปุ่มของคุณมีตัวจดจำท่าทางของตัวเอง (น่าจะเป็น) ให้ขุดเข้าไปในภายในของพวกเขาและจับพวกมันและอ่านเอกสาร-[UIGestureRecognizer requireGestureRecognizerToFail:]แต่อาจใช้งานได้โดยไม่ต้องดัดแปลง
bshirley

กำลังมองหาสิ่งนี้เป็นเวลาหลายชั่วโมง! ขอบคุณ! ;)
MasterRazer

หากifการทดสอบของคุณล้มเหลวการดำเนินการของคุณไม่สามารถส่งคืน BOOL ได้ สำหรับการส่งคืนรูปแบบที่เหมาะสมใช่หลังจากบล็อก if (ใช้{ }) หรือในelseสาขา ขอบคุณที่ช่วยให้ฉันอ่านได้เล็กน้อย
RobP

2
มุมมองเป็นลูกหลานของตัวเองดังนั้นจึงไม่ได้ผล ... (แนวทางโดยรวมก็โอเคดูคำตอบอื่น ๆ )
Ixx

109

อีกวิธีหนึ่งคือเพียงเปรียบเทียบว่ามุมมองของการสัมผัสเป็นมุมมองท่าทางเพราะลูกหลานจะไม่ผ่านเงื่อนไข หนึ่งซับที่ดีและเรียบง่าย:

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

2
สิ่งนี้ใช้ได้ผลดีกว่าสำหรับฉันมากกว่าคำตอบที่ยอมรับ กระชับกว่ามาก
Suman Roy

1
@ เอียนวิธีนี้ไม่ถูกเรียกใช้เมื่อแตะที่มุมมอง
ระบุราจ

1
คำตอบที่กระชับ!
scurioni

คำตอบที่ได้รับการยอมรับไม่ได้ผล แต่ก็ใช้ได้ผลสำหรับฉันอย่างไม่มีที่ติ
Shalin Shah

@Prabu เป็นไปได้เพราะต้องมีการตั้งตัวแทนของเครื่องมือจดจำท่าทางก่อน
Kubba

23

และสำหรับรุ่น Swift:

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

นานาน่ารู้isDescendantOfViewส่งคืนBooleanค่าที่ระบุว่าผู้รับเป็นมุมมองย่อยของมุมมองที่กำหนดหรือเหมือนกับมุมมองนั้น


1
อย่าลืมตั้งค่า UIGestureRecognizerDelegate ในชั้นเรียนของคุณ!
Fox5150

4
แทนที่จะทำอย่างนั้นถ้าคำสั่งคุณไม่สามารถคืนสิ่งนี้ return! touch.view.isDescendant (ของ: ท่าทางRecognizer.view) นอกจากนี้ไวยากรณ์ใหม่ใน swift 3 ^^
EdwardSanchez

13

ด้วย Swift 5 และ iOS 12 UIGestureRecognizerDelegateมีวิธีการที่เรียกว่าgestureRecognizer(_:shouldReceive:). gestureRecognizer(_:shouldReceive:)มีประกาศดังต่อไปนี้:

ถามผู้รับมอบสิทธิ์ว่าเครื่องจดจำท่าทางควรได้รับวัตถุที่แสดงถึงการสัมผัสหรือไม่

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

gestureRecognizer(_:shouldReceive:)รหัสที่สมบูรณ์ด้านล่างแสดงการดำเนินงานที่เป็นไปได้สำหรับ ด้วยรหัสนี้การแตะที่ViewControllerมุมมองย่อยของมุมมอง (รวมถึงimageView) จะไม่เรียกใช้printHello(_:)เมธอด

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

}

การใช้งานทางเลือกgestureRecognizer(_:shouldReceive:)สามารถ:

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

อย่างไรก็ตามโปรดทราบว่ารหัสทางเลือกนี้ไม่ได้ตรวจสอบว่าtouch.viewเป็นมุมมองย่อยของgestureRecognizer.view.


10

โซลูชันที่รวดเร็วสมบูรณ์ (ต้องใช้ผู้ร่วมประชุมและตั้งค่าสำหรับโปรแกรมจดจำ):

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

ตัวแปรโดยใช้ CGPoint ที่คุณสัมผัส (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

ล้างวิธี Swift

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

ไม่เคยถูกเรียกใน iOS 13 อย่างน้อยก็มี SwipeGestureRecognizer
Chewie The Chorkie

สิ่งนี้แตกต่างกับคำตอบของ Ian อย่างไร?
sweetfa

3

โปรดสังเกตว่าGestureRecognizer APIเปลี่ยนเป็น:

ท่าทางRecognizer (_: shouldReceive :)

จดบันทึกเครื่องหมายขีดล่าง (ข้าม) สำหรับป้ายกำกับภายนอกของพารามิเตอร์ตัวแรก

จากตัวอย่างที่ให้ไว้ข้างต้นฉันไม่ได้รับกิจกรรม ด้านล่างนี้เป็นตัวอย่างที่ใช้ได้กับ 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

นอกจากนี้วิธีแก้ไขปัญหาข้างต้นอย่าลืมตรวจสอบUser Interaction Enabledมุมมองย่อยของคุณ

ป้อนคำอธิบายภาพที่นี่


2

ฉันต้องป้องกันไม่ให้ท่าทางในมุมมองของเด็ก สิ่งเดียวที่ได้ผลคืออนุญาตและรักษามุมมองแรกและป้องกันท่าทางสัมผัสในมุมมองถัดไปทั้งหมด:

   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 ตอนนี้เป็นทางเลือกดังนั้นตามคำตอบของ @ Antoine:

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

0

หากคุณไม่ต้องการให้ 'ตัวตรวจจับการแตะสองครั้ง' ขัดแย้งกับปุ่มและ / หรือการควบคุมอื่น ๆ คุณสามารถตั้งค่าselfเป็นUIGestureRecognizerDelegateและใช้งานได้:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
{
    return !(touch.view is UIControl)
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.