Câu hỏi tương tác vượt quá giới hạn của uiview


Có thể cho một UIButton (hoặc bất kỳ điều khiển khác cho rằng vấn đề) để nhận các sự kiện liên lạc khi khung của UIButton nằm bên ngoài khung của cha mẹ nó? Nguyên nhân khi tôi thử điều này, UIButton của tôi dường như không thể nhận bất kỳ sự kiện nào. Tôi làm cách nào để giải quyết vấn đề này?


76
2018-03-25 13:21


gốc


Điều này làm việc hoàn hảo cho tôi. developer.apple.com/library/ios/qa/qa2013/qa1812.html Tốt - Frank Li
Điều này cũng tốt và có liên quan (hoặc có thể là một bản dupe): stackoverflow.com/a/31420820/8047 - Dan Rosenstark


Các câu trả lời:


Vâng. Bạn có thể ghi đè lên hitTest:withEvent: phương thức trả về một khung nhìn cho một tập hợp lớn hơn các điểm có chứa. Xem Tham chiếu lớp UIView.

Chỉnh sửa: Thí dụ:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    CGFloat radius = 100.0;
    CGRect frame = CGRectMake(-radius, -radius,
                              self.frame.size.width + radius,
                              self.frame.size.height + radius);

    if (CGRectContainsPoint(frame, point)) {
        return self;
    }
    return nil;
}

Chỉnh sửa 2: (Sau khi làm rõ :) Để đảm bảo rằng nút được xử lý như nằm trong giới hạn của cha mẹ, bạn cần phải ghi đè pointInside:withEvent: trong phụ huynh để bao gồm khung của nút.

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    if (CGRectContainsPoint(self.view.bounds, point) ||
        CGRectContainsPoint(button.view.frame, point))
    {
        return YES;
    }
    return NO;
}

chú thích mã chỉ ở đó để ghi đè pointInside không hoàn toàn chính xác. Như Summon giải thích dưới đây, hãy làm như sau:

-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
    {
    if ( CGRectContainsPoint(self.oversizeButton.frame, point) )
        return YES;

    return [super pointInside:point withEvent:event];
    }

Lưu ý rằng bạn rất có thể sẽ làm điều đó với self.oversizeButton dưới dạng IBOutlet trong lớp con UIView này; sau đó bạn có thể chỉ cần kéo "nút quá khổ" trong câu hỏi, đến, chế độ xem đặc biệt được đề cập. (Hoặc, nếu vì lý do nào đó bạn đã làm điều này rất nhiều trong một dự án, bạn sẽ có một lớp con UIButton đặc biệt, và bạn có thể xem qua danh sách subview của bạn cho các lớp đó.) Hy vọng nó sẽ giúp.


83
2018-03-25 13:36



Bạn có thể giúp tôi thêm về việc thực hiện điều này một cách chính xác với một số mã mẫu không? Ngoài ra tôi đã đọc tài liệu tham khảo và vì dòng sau, tôi bối rối: "Những điểm nằm ngoài giới hạn của người nhận không bao giờ được báo cáo là lượt truy cập, ngay cả khi chúng thực sự nằm trong một trong các phần phụ của người nhận. của cha mẹ của họ nếu thuộc tính clipToBounds của chế độ xem của cha được đặt thành NO. Tuy nhiên, kiểm tra lần truy cập luôn bỏ qua các điểm bên ngoài giới hạn của chế độ xem của phụ huynh ". - ThomasM
Nó đặc biệt là phần này làm cho nó có vẻ như đây không phải là giải pháp tôi cần: "Tuy nhiên, thử nghiệm hit luôn bỏ qua các điểm bên ngoài giới hạn của chế độ xem của phụ huynh" .. Nhưng tôi có thể là lời nói sai, tôi thấy tài liệu tham khảo táo đôi khi thực sự khó hiểu .. - ThomasM
Ah, tôi hiểu rồi. Bạn cần ghi đè lên pointInside:withEvent: để bao gồm khung của nút. Tôi sẽ thêm một số mã mẫu vào câu trả lời của tôi ở trên. - jnic
Có cách nào để làm điều này mà không có phương pháp ghi đè của một UIView? Ví dụ, nếu quan điểm của bạn là hoàn toàn chung chung với UIKit và khởi tạo thông qua một Nib, bạn có thể ghi đè lên các phương pháp này từ bên trong UIViewController của bạn? - RLH
Bạn không thể ghi đè trực tiếp các phương thức; tuy nhiên, bạn có thể ghi đè touchesBegan v.v. UIViewController là một UIResponder lớp con. Chế độ xem được khởi tạo trực tiếp từ ngòi bút vẫn có thể là các lớp con tùy chỉnh. Chỉ cần đặt trường Lớp của chế độ xem của bạn trong Trình kiểm tra danh tính làm lớp con tùy chỉnh của bạn và sau khi tải, hãy truyền nib của bạn cho phù hợp. - jnic


@ jnic, tôi đang làm việc trên iOS SDK 5.0 và để mã của bạn hoạt động đúng, tôi phải thực hiện việc này:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
if (CGRectContainsPoint(button.frame, point)) {
    return YES;
}
return [super pointInside:point withEvent:event]; }

Khung nhìn container trong trường hợp của tôi là một UIButton và tất cả các phần tử con cũng là các UIButton có thể di chuyển ra ngoài các giới hạn của UIButton mẹ.

Tốt


23
2018-02-02 14:22





Trong chế độ xem gốc, bạn có thể ghi đè phương pháp kiểm tra lượt truy cập:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    CGPoint translatedPoint = [_myButton convertPoint:point fromView:self];

    if (CGRectContainsPoint(_myButton.bounds, translatedPoint)) {
        return [_myButton hitTest:translatedPoint withEvent:event];
    }
    return [super hitTest:point withEvent:event];

}

Trong trường hợp này, nếu điểm nằm trong giới hạn của nút của bạn, bạn sẽ chuyển tiếp cuộc gọi đến đó; nếu không, hãy hoàn nguyên về việc triển khai ban đầu.


16
2018-05-25 04:17





Nhanh:

Trường hợp targetView là khung nhìn mà bạn muốn nhận sự kiện (có thể nằm hoàn toàn hoặc một phần nằm ngoài khung của chế độ xem chính).

override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
        let pointForTargetView: CGPoint = targetView.convertPoint(point, fromView: self)
        if CGRectContainsPoint(targetView.bounds, pointForTargetView) {
            return closeButton
        }
        return super.hitTest(point, withEvent: event)
}

7
2018-04-25 19:26





Trong trường hợp của tôi, tôi đã có một UICollectionViewCell lớp con chứa UIButton. Tôi đã tắt clipsToBounds trên ô và nút có thể nhìn thấy bên ngoài ranh giới của ô. Tuy nhiên, nút không nhận được sự kiện chạm. Tôi đã có thể phát hiện các sự kiện cảm ứng trên nút bằng cách sử dụng câu trả lời của Jack: https://stackoverflow.com/a/30431157/3344977

Đây là phiên bản Swift:

override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {

    let translatedPoint = button.convertPoint(point, fromView: self)

    if (CGRectContainsPoint(button.bounds, translatedPoint)) {
        print("Your button was pressed")
        return button.hitTest(translatedPoint, withEvent: event)
    }
    return super.hitTest(point, withEvent: event)
}

5
2017-11-30 23:29





Tại sao điều này xảy ra?

Điều này là do khi tiểu sử của bạn nằm ngoài giới hạn của người giám sát, hãy chạm vào các sự kiện thực sự xảy ra trên tiểu sử đó sẽ không được gửi tới tiểu sử đó. Tuy nhiên, nó SẼ được gửi đến người giám sát của nó.

Bất kể các bản xem phụ có bị cắt bớt trực quan hay không, các sự kiện chạm luôn luôn tôn trọng hình chữ nhật có đường viền của người giám sát của chế độ xem mục tiêu. Nói cách khác, các sự kiện chạm xảy ra trong một phần của chế độ xem nằm ngoài hình chữ nhật viền của người giám sát sẽ không được phân phối đến chế độ xem đó. Liên kết

Bạn cần gì để làm?

Khi người giám sát của bạn nhận được sự kiện liên lạc được đề cập ở trên, bạn cần phải thông báo cho UIKit rõ ràng rằng tiểu sử của tôi phải là người nhận sự kiện cảm ứng này.

Điều gì về mã?

Trong giám sát của bạn, triển khai func hitTest(_ point: CGPoint, with event: UIEvent?)

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        if isHidden || alpha == 0 || clipsToBounds { return nil }
        // convert the point into subview's coordinate system
        let subviewPoint = self.convert(point, to: subview)
        // if the converted point lies in subview's bound, tell UIKit that subview should be the one that receives this event
        if ! subview.isHidden && subview.bounds.contains(subviewPoint) { return subview }
        return nil
    }

3
2018-01-10 22:46



Cảm ơn vì điều này! Tôi đã dành hàng giờ đồng hồ cho một vấn đề xuất phát từ vấn đề này. Tôi thực sự đánh giá cao việc bạn dành thời gian để đăng bài này. :) - ClayJ


Đối với tôi (đối với những người khác), câu trả lời là không phải để ghi đè lên hitTest phương pháp, mà đúng hơn là pointInside phương pháp. Tôi phải làm điều này chỉ ở hai nơi.

The Superview Đây là quan điểm cho rằng nếu nó có clipsToBounds đặt thành true, nó sẽ làm cho toàn bộ vấn đề này biến mất. Vì vậy, đây là đơn giản của tôi UIView trong Swift 3:

class PromiscuousView : UIView {
    override func point(inside point: CGPoint,
               with event: UIEvent?) -> Bool {
        return true
    }
} 

Subview Số dặm của bạn có thể thay đổi, nhưng trong trường hợp của tôi, tôi cũng phải ghi đè lên pointInside phương pháp cho các subview đã được quyết định liên lạc đã được ra khỏi giới hạn. Mỏ nằm trong Objective-C, vì vậy:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    WEPopoverController *controller = (id) self.delegate;
    if (self.takeHitsOutside) {
        return YES;
    } else {
        return [super pointInside:point withEvent:event];
    }
}

Câu trả lời này (và một vài người khác trên cùng một câu hỏi) có thể giúp bạn hiểu rõ hơn.


0
2018-01-09 15:13