UIButton bên trong chế độ xem có UITapGestureRecognizer


184

Tôi có quan điểm với a UITapGestureRecognizer. Vì vậy, khi tôi nhấn vào khung nhìn, một khung nhìn khác xuất hiện phía trên khung nhìn này. Chế độ xem mới này có ba nút. Khi tôi nhấn vào một trong các nút này, tôi không nhận được hành động của các nút, tôi chỉ nhận được hành động cử chỉ nhấn. Vì vậy, tôi không thể sử dụng các nút này nữa. Tôi có thể làm gì để có được các sự kiện thông qua các nút này? Điều kỳ lạ là các nút vẫn được làm nổi bật.

Tôi không thể xóa UITapGestureRecognizer sau khi tôi nhận được. Bởi vì với nó, quan điểm mới cũng có thể được gỡ bỏ. Có nghĩa là tôi muốn một hành vi như các điều khiển vide toàn màn hình .

Câu trả lời:


256

Bạn có thể đặt bộ điều khiển hoặc chế độ xem (tùy theo đó tạo bộ nhận dạng cử chỉ) làm đại biểu của UITapGestureRecognizer. Sau đó, trong đại biểu bạn có thể thực hiện -gestureRecognizer:shouldReceiveTouch:. Trong quá trình thực hiện, bạn có thể kiểm tra xem cảm ứng có thuộc về chế độ xem mới của bạn không và nếu có, hãy hướng dẫn trình nhận dạng cử chỉ bỏ qua nó. Một cái gì đó như sau:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    // test if our control subview is on-screen
    if (self.controlSubview.superview != nil) {
        if ([touch.view isDescendantOfView:self.controlSubview]) {
            // we touched our control surface
            return NO; // ignore the touch
        }
    }
    return YES; // handle the touch
}

Trong tệp tiêu đề của tôi, tôi đã có quan điểm của mình triển khai UIGestoreRegognizerDelegate và trong .mi của tôi đã thêm mã của bạn ở trên. Vòi không bao giờ đi vào phương pháp này, nó đi thẳng vào xử lý của tôi. Có ý kiến ​​gì không?
kmehta

1
@kmehta rất có thể bạn đã quên đặt thuộc tính đại biểu UIGestureRecognizer.
Đến

Câu trả lời này có thể được khái quát để xử lý tất cả các trường hợp có IBAction liên quan không?
Martin Wickman

@Martin IBAction biên dịch xuống để voidnó không để lại bất kỳ thông tin nào trong thời gian chạy để sử dụng để phát hiện. Nhưng những gì bạn có thể làm là đi lên phân cấp xem, kiểm tra UIControlvà quay lại NOnếu bạn tìm thấy bất kỳ điều khiển nào.
Lily Ballard

Thx cho điều này, nó giúp tôi. nếu nút của bạn ở trong UIImageView, hãy đảm bảo nếu userInteractionEnables của imageView được đặt ở CÓ.
james075

156

Theo dõi câu trả lời của Casey theo câu trả lời của Kevin Ballard:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
        if ([touch.view isKindOfClass:[UIControl class]]) {
            // we touched a button, slider, or other UIControl
            return NO; // ignore the touch
        }
    return YES; // handle the touch
}

Điều này về cơ bản làm cho tất cả các loại điều khiển đầu vào của người dùng như nút, thanh trượt, v.v.


1
Đó là một giải pháp linh hoạt có thể đi kèm setCancelsTouchesInView = NOđể không kích hoạt cử chỉ nhấn khi tương tác với các điều khiển. Tuy nhiên, bạn có thể viết nó tốt hơn:return ![touch.view isKindOfClass:[UIControl class]];
griga13

Giải pháp Swift 4+: trở lại! (Touch.view là UIControl)
nteissler

103

Tìm thấy câu trả lời ở đây: link

Bạn cũng có thể dùng

tapRecognizer.cancelsTouchesInView = NO;

Điều này ngăn cản bộ nhận dạng vòi là người duy nhất bắt được tất cả các vòi

CẬP NHẬT - Michael đã đề cập đến liên kết đến tài liệu mô tả thuộc tính này: cancelsTouchesInView


8
Đây phải là câu trả lời được chấp nhận IMO. Điều này cho phép mọi chế độ xem phụ tự xử lý vòi và ngăn chế độ xem có trình nhận dạng tab được gắn vào nó khỏi 'highjacking' vòi. Không cần phải thực hiện một ủy nhiệm nếu tất cả những gì bạn muốn làm là tạo một chế độ xem có thể nhấp (để từ bỏ phản hồi đầu tiên / ẩn bàn phím trên trường văn bản, v.v.) .. tuyệt vời!
EeKay

4
Điều này chắc chắn nên là câu trả lời được chấp nhận. Câu trả lời này đã khiến tôi đọc tài liệu của Apple đúng cách, điều này cho thấy rõ rằng trình nhận dạng cử chỉ sẽ ngăn các cuộc phỏng vấn nhận được các sự kiện được công nhận trừ khi bạn làm điều này.
Michael van der Westhuizen

1
Điều này được sử dụng để làm việc nhưng bây giờ nó không hoạt động đối với tôi..chỉ vì tôi có một cuộn Xem dưới nút? Tôi đã phải thực hiện các đại biểu như trong các câu trả lời ở trên.
xissburg

7
Sau khi thử nghiệm một chút, tôi nhận thấy rằng điều này sẽ chỉ hoạt động nếu chế độ xem chứa trình nhận dạng cử chỉ là anh chị em của UIControl và sẽ không thành công nếu chế độ xem là cha mẹ của UIControl.
xissburg

6
Không thực sự hoạt động như dự định ... CÓ, nó ngăn không cho người nhận biết để ăn sự kiện trên UIControl. Nhưng nó vẫn chạy hành động nhận dạng, đó là chạy 2 hành động cùng một lúc.
Tek Yin

71

Để theo dõi câu trả lời của Kevin Ballard, tôi đã gặp vấn đề tương tự và cuối cùng đã sử dụng mã này:

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

Nó có tác dụng tương tự nhưng điều này sẽ hoạt động trên bất kỳ UIButton nào ở bất kỳ độ sâu xem nào (UIButton của tôi có một số lượt xem sâu và đại biểu của UIGestureRecognizer không có tham chiếu đến nó.)


6
Đừng muốn trở thành một kẻ tinh ranh ở đây, nhưng nó thực sự CÓ và KHÔNG. Vui lòng sử dụng quy ước ca cao. TRUE / FALSE / NULL dành cho lớp CoreFoundation.
steipete

từ những gì tôi đã đọc, CÓ và KHÔNG thực sự được tạo ra để dễ đọc. khả năng đọc là chủ quan. nó bắt nguồn từ các quy ước đặt tên phương thức và tài sản mà không phải ai cũng quá quan tâm. nếu bạn muốn tuân thủ nghiêm ngặt quy ước đặt tên thì có, hãy sử dụng CÓ và KHÔNG cho bất kỳ điều gì ở NSW. tuy nhiên, tôi sẽ nói rằng nếu bạn khảo sát các nhà phát triển, bạn S get nhận được nhiều ý kiến ​​trái chiều về việc một trong số đó dễ đọc hơn [nút setHidden: YES]; -hoặc- [nút setHidden: TRUE];
Pimp Juice McJones

1
Tôi đoán điều tôi đang cố nói là nếu tiền đề của các quy ước đặt tên là dễ đọc thì tôi nghĩ thật khó để phủ nhận rằng nó chủ quan trong một số trường hợp. nếu bạn là người theo chủ nghĩa thuần túy thì bạn sẽ không hiểu câu nói đó.
Pimp Juice McJones

2
Điều này có vẻ chủ quan nhưng sau một thời gian lập trình Cacao, bạn thực sự hòa vào dòng mã chính xác hơn về mặt ngữ nghĩa ... "TRUE" dường như thực sự sai sau nhiều năm của Objective-C, bởi vì nó không khớp với "ẩn" tốt.
Kendall Helmstetter Gelner

Từ khi nào bạn có thể / bạn chuyển một cử chỉ nhấn vào UIButton dưới dạng Sự kiện Touch Up Inside UITouch (sự kiện sau này là sự kiện được tạo bằng cách nhấn vào nút UIB)? Có vẻ trùng lặp và không chính xác khi tạo trình nhận dạng cử chỉ nhấn cho nút có 15 sự kiện chạm đã được liên kết với cử chỉ nhấn. Tôi muốn xem mã, không đoán.
James Bush

10

Trong iOS 6.0 trở lên, các hành động kiểm soát mặc định ngăn hành vi nhận dạng cử chỉ chồng chéo. Ví dụ: hành động mặc định cho một nút là một lần chạm. Nếu bạn có một trình nhận dạng cử chỉ chạm duy nhất được gắn vào chế độ xem chính của nút và người dùng nhấn nút, thì phương thức hành động của nút sẽ nhận sự kiện chạm thay vì nhận dạng cử chỉ. Điều này chỉ áp dụng cho nhận dạng cử chỉ chồng lấp hành động mặc định cho điều khiển, bao gồm: .....

Từ tài liệu API của Apple


8

Những câu trả lời không đầy đủ. Tôi đã phải đọc nhiều bài viết về cách sử dụng thao tác boolean này.

Trong tệp * .h của bạn thêm điều này

@interface v1ViewController : UIViewController <UIGestureRecognizerDelegate>

Trong tệp * .m của bạn thêm điều này

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {

    NSLog(@"went here ...");

    if ([touch.view isKindOfClass:[UIControl class]])
    {
        // we touched a button, slider, or other UIControl
        return NO; // ignore the touch
    }
    return YES; // handle the touch
}
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.



    //tap gestrure
    UITapGestureRecognizer *tapGestRecog = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(screenTappedOnce)];
    [tapGestRecog setNumberOfTapsRequired:1];
    [self.view addGestureRecognizer:tapGestRecog];


// This line is very important. if You don't add it then your boolean operation will never get called
tapGestRecog.delegate = self;

}


-(IBAction) screenTappedOnce
{
    NSLog(@"screenTappedOnce ...");

}

7

Tìm thấy một cách khác để làm điều đó từ đây . Nó phát hiện cảm ứng cho dù bên trong mỗi nút hay không.

(1) pointInside: withEvent: (2) locationInView:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
       shouldReceiveTouch:(UITouch *)touch {
    // Don't recognize taps in the buttons
    return (![self.button1 pointInside:[touch locationInView:self.button1] withEvent:nil] &&
            ![self.button2 pointInside:[touch locationInView:self.button2] withEvent:nil] &&
            ![self.button3 pointInside:[touch locationInView:self.button3] withEvent:nil]);
}

Tôi đã thử tất cả các giải pháp khác cho việc này, nhưng phương pháp -pointInside: withEvent: là cách duy nhất hiệu quả với tôi.
Kenny Wyland

3

Đây là phiên bản Swift của câu trả lời của Lily Ballard phù hợp với tôi:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if (scrollView.superview != nil) {
        if ((touch.view?.isDescendantOfView(scrollView)) != nil) { return false }
    }
    return true
}

3

Swift 5

Nút trên giám sát với tapesture

 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if let _ = touch.view as? UIButton { return false }
    return true
}

Trong trường hợp của tôi, việc thực hiện hitTest đã làm việc cho tôi. Tôi đã xem bộ sưu tập với nút

Phương thức này đi qua hệ thống phân cấp khung nhìn bằng cách gọi point(inside:with:)phương thức của mỗi khung nhìn phụ để xác định xem khung nhìn con nào sẽ nhận được một sự kiện chạm. Nếupoint(inside:with:) trả về giá trị true, thì hệ thống phân cấp của khung nhìn phụ cũng tương tự đi qua cho đến khi chế độ xem trước có chứa điểm được chỉ định được tìm thấy.

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    guard isUserInteractionEnabled else { return nil }

    guard !isHidden else { return nil }

    guard alpha >= 0.01 else { return nil }

    guard self.point(inside: point, with: event) else { return nil }

    for eachImageCell in collectionView.visibleCells {
        for eachImageButton in eachImageCell.subviews {
            if let crossButton = eachImageButton as? UIButton {
                if crossButton.point(inside: convert(point, to: crossButton), with: event) {
                    return crossButton
                }
            }
        }
    }
    return super.hitTest(point, with: event)
}

1

Bạn có thể ngăn UITapGestureRecognizer hủy các sự kiện khác (chẳng hạn như nhấn vào nút của bạn) bằng cách đặt boolean sau:

    [tapRecognizer setCancelsTouchesInView:NO];

1

Nếu kịch bản của bạn là như thế này:

Bạn có chế độ xem đơn giản và một số nút UIB, các điều khiển UITextField được thêm dưới dạng xem trước cho chế độ xem đó. Bây giờ bạn muốn tắt bàn phím khi bạn chạm vào bất kỳ nơi nào khác trên màn hình ngoại trừ trên các điều khiển (các cuộc phỏng vấn bạn đã thêm)

Giải pháp là:

Thêm phương thức sau vào XYZViewControll.m (có chế độ xem của bạn)

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.view endEditing:YES];
}

Đầu tiên tôi đã thử với câu trả lời của @cdasher nhưng trong trường hợp của tôi ngay cả khi nhấp vào nút, tôi vẫn nhận được touch.view dưới dạng "view" của tôi trong ViewContoder nhưng không phải là điều khiển "UIButton" của tôi. Ai đó có thể nói lý do tại sao thuộc tính chế độ xem của cảm ứng không trả về chế độ xem ban đầu trong đó chạm xảy ra không
NaveenRaghuveer

1
Sẽ không hoạt động nếu bạn có cuộn xem hoặc một số chế độ xem khác yêu cầu các sự kiện chạm.
lkraider

1

Tối ưu hóa câu trả lời của cdasher, bạn nhận được

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
       shouldReceiveTouch:(UITouch *)touch 
{
    return ![touch.view isKindOfClass:[UIControl class]];
}
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.