Câu hỏi Chụp chạm trên một khung nhìn bên ngoài khung của superview của nó bằng cách sử dụng hitTest: withEvent:
Vấn đề của tôi: Tôi có một người giám sát EditView
chiếm toàn bộ khung ứng dụng và một khung nhìn con MenuView
chỉ chiếm ~ 20%, và sau đó MenuView
chứa subview riêng của nó ButtonView
mà thực sự nằm bên ngoài MenuView
's giới hạn (một cái gì đó như thế này: ButtonView.frame.origin.y = -100
).
(chú thích: EditView
có các bản xem phụ khác không phải là một phần của MenuView
's xem hệ thống phân cấp, nhưng có thể ảnh hưởng đến câu trả lời.)
Có thể bạn đã biết vấn đề: khi nào ButtonView
nằm trong giới hạn của MenuView
(hoặc, cụ thể hơn, khi chạm của tôi nằm trong MenuView
giới hạn), ButtonView
phản hồi các sự kiện liên lạc. Khi chạm của tôi ở bên ngoài MenuView
giới hạn của (nhưng vẫn nằm trong ButtonView
's giới hạn), không có sự kiện cảm ứng được nhận bởi ButtonView
.
Thí dụ:
- (E) là
EditView
, cha mẹ của tất cả các chế độ xem
- (M) là
MenuView
, một chế độ xem phụ của EditView
- (B) là
ButtonView
, một chế độ xem con của MenuView
Sơ đồ:
+------------------------------+
|E |
| |
| |
| |
| |
|+-----+ |
||B | |
|+-----+ |
|+----------------------------+|
||M ||
|| ||
|+----------------------------+|
+------------------------------+
Bởi vì (B) nằm ngoài khung (M), một vòi ở khu vực (B) sẽ không bao giờ được gửi đến (M) - trên thực tế, (M) không bao giờ phân tích cảm ứng trong trường hợp này, và cảm ứng được gửi đến đối tượng tiếp theo trong cấu trúc phân cấp.
Mục tiêu: Tôi thu thập điều đó hitTest:withEvent:
có thể giải quyết vấn đề này, nhưng tôi không hiểu chính xác như thế nào. Trong trường hợp của tôi, nên hitTest:withEvent:
được ghi đè trong EditView
(giám sát 'chủ nhân' của tôi)? Hoặc nó nên được ghi đè trong MenuView
, người giám sát trực tiếp của nút không nhận được chạm? Hay tôi đang nghĩ về điều này không chính xác?
Nếu điều này đòi hỏi một lời giải thích dài dòng, một nguồn tài nguyên trực tuyến tốt sẽ hữu ích - ngoại trừ tài liệu UIView của Apple, những tài liệu này chưa làm rõ với tôi.
Cảm ơn!
76
2017-08-02 03:41
gốc
Các câu trả lời:
Tôi đã sửa đổi mã của câu trả lời được chấp nhận chung hơn - nó xử lý các trường hợp mà chế độ xem clip xem các giới hạn của nó, có thể bị ẩn và quan trọng hơn: nếu các bản xem phụ là cấu trúc phân cấp phức tạp, thì chế độ xem chính xác sẽ được trả lại.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (self.clipsToBounds) {
return nil;
}
if (self.hidden) {
return nil;
}
if (self.alpha == 0) {
return nil;
}
for (UIView *subview in self.subviews.reverseObjectEnumerator) {
CGPoint subPoint = [subview convertPoint:point fromView:self];
UIView *result = [subview hitTest:subPoint withEvent:event];
if (result) {
return result;
}
}
return nil;
}
SWIFT 3
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if clipsToBounds || isHidden || alpha == 0 {
return nil
}
for subview in subviews.reversed() {
let subPoint = subview.convert(point, from: self)
if let result = subview.hitTest(subPoint, with: event) {
return result
}
}
return nil
}
Tôi hy vọng điều này sẽ giúp bất cứ ai đang cố gắng sử dụng giải pháp này cho các trường hợp sử dụng phức tạp hơn.
123
2018-02-14 13:13
Ok, tôi đã làm một số đào và thử nghiệm, đây là cách hitTest:withEvent
công trình - ít nhất ở mức cao. Hình ảnh kịch bản này:
- (E) là EditView, cha mẹ của tất cả các chế độ xem
- (M) là MenuView, một chế độ xem phụ của EditView
- (B) là ButtonView, một chế độ xem con của MenuView
Sơ đồ:
+------------------------------+
|E |
| |
| |
| |
| |
|+-----+ |
||B | |
|+-----+ |
|+----------------------------+|
||M ||
|| ||
|+----------------------------+|
+------------------------------+
Bởi vì (B) nằm ngoài khung (M), một vòi ở khu vực (B) sẽ không bao giờ được gửi đến (M) - trên thực tế, (M) không bao giờ phân tích cảm ứng trong trường hợp này, và cảm ứng được gửi đến đối tượng tiếp theo trong cấu trúc phân cấp.
Tuy nhiên, nếu bạn triển khai hitTest:withEvent:
trong (M), vòi bất cứ nơi nào trong ứng dụng sẽ được gửi đến (M) (hoặc ít nhất nó biết về chúng). Bạn có thể viết mã để xử lý các liên lạc trong trường hợp đó và trả lại đối tượng mà sẽ nhận được liên lạc.
Cụ thể hơn: mục tiêu của hitTest:withEvent:
là trả về đối tượng sẽ nhận được lần truy cập. Vì vậy, trong (M) bạn có thể viết mã như thế này:
// need this to capture button taps since they are outside of self.frame
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
for (UIView *subview in self.subviews) {
if (CGRectContainsPoint(subview.frame, point)) {
return subview;
}
}
// use this to pass the 'touch' onward in case no subviews trigger the touch
return [super hitTest:point withEvent:event];
}
Tôi vẫn còn rất mới với phương pháp này và vấn đề này, vì vậy nếu có cách hiệu quả hơn hoặc chính xác để viết mã, xin vui lòng bình luận.
Tôi hy vọng rằng sẽ giúp bất cứ ai khác truy cập câu hỏi này sau. :)
26
2017-08-03 17:39
Trong Swift 4
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard !clipsToBounds && !isHidden && alpha > 0 else { return nil }
for member in subviews.reversed() {
let subPoint = member.convert(point, from: self)
guard let result = member.hitTest(subPoint, with: event) else { continue }
return result
}
return nil
}
18
2018-03-28 14:24
Những gì tôi sẽ làm là có cả ButtonView và MenuView tồn tại ở cùng cấp trong hệ thống phân cấp khung nhìn bằng cách đặt chúng cả trong một thùng chứa có khung hoàn toàn phù hợp với cả hai. Bằng cách này, khu vực tương tác của mục được cắt bớt sẽ không bị bỏ qua vì đó là ranh giới của người giám sát.
2
2017-08-02 04:07
Nếu có ai cần, đây là giải pháp thay thế nhanh chóng
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
if !self.clipsToBounds && !self.hidden && self.alpha > 0 {
for subview in self.subviews.reverse() {
let subPoint = subview.convertPoint(point, fromView:self);
if let result = subview.hitTest(subPoint, withEvent:event) {
return result;
}
}
}
return nil
}
0
2017-08-26 17:16
Nếu bạn có nhiều subviews khác trong view cha của bạn thì có lẽ hầu hết các view tương tác khác sẽ không hoạt động nếu bạn sử dụng các giải pháp trên, trong trường hợp đó bạn có thể sử dụng một cái gì đó như thế này (Trong Swift 3.2):
class BoundingSubviewsViewExtension: UIView {
@IBOutlet var targetView: UIView!
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// Convert the point to the target view's coordinate system.
// The target view isn't necessarily the immediate subview
let pointForTargetView: CGPoint? = targetView?.convert(point, from: self)
if (targetView?.bounds.contains(pointForTargetView!))! {
// The target view may have its view hierarchy,
// so call its hitTest method to return the right hit-test view
return targetView?.hitTest(pointForTargetView ?? CGPoint.zero, with: event)
}
return super.hitTest(point, with: event)
}
}
0
2017-11-03 06:51
Đặt các dòng mã dưới đây vào phân cấp chế độ xem của bạn:
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
UIView* hitView = [super hitTest:point withEvent:event];
if (hitView != nil)
{
[self.superview bringSubviewToFront:self];
}
return hitView;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event
{
CGRect rect = self.bounds;
BOOL isInside = CGRectContainsPoint(rect, point);
if(!isInside)
{
for (UIView *view in self.subviews)
{
isInside = CGRectContainsPoint(view.frame, point);
if(isInside)
break;
}
}
return isInside;
}
Để giải thích rõ hơn, nó đã được giải thích trong blog của tôi: "goaheadwithiphonetech" về "Chú dẫn tùy chỉnh: Nút không phải là vấn đề có thể nhấp".
Tôi hy vọng rằng sẽ giúp bạn...!!!
-1
2018-05-19 10:50