tob*_*pwn 91 objective-c ios uiview-hierarchy
我的问题:我有一个超级视图EditView,基本上占用整个应用程序框架,子视图MenuView只占用底部〜20%,然后MenuView包含它自己的子视图ButtonView,它实际上位于MenuView边界之外(类似这样:) ButtonView.frame.origin.y = -100.
(注意:EditView其他子视图不属于MenuView视图层次结构,但可能会影响答案.)
您可能已经知道了这个问题:何时ButtonView在MenuView(或者更具体地说,当我的触摸在MenuView范围内)时,ButtonView响应触摸事件.当我的触摸超出MenuView界限(但仍然在ButtonView界限内)时,不会收到任何触摸事件ButtonView.
例:
EditView所有观点的父母MenuViewEditView的子视图ButtonViewMenuView的子视图图:
+------------------------------+
|E |
| |
| |
| |
| |
|+-----+ |
||B | |
|+-----+ |
|+----------------------------+|
||M ||
|| ||
|+----------------------------+|
+------------------------------+
Run Code Online (Sandbox Code Playgroud)
因为(B)在(M)的框架之外,(B)区域中的抽头将永远不会被发送到(M) - 实际上,(M)在这种情况下从不分析触摸,并且触摸被发送到层次结构中的下一个对象.
目标:我认为压倒一切hitTest:withEvent:可以解决这个问题,但我不明白究竟是怎么回事.在我的情况下,应该hitTest:withEvent:被覆盖EditView(我的'主'超级视图)?或者是否应该覆盖MenuView,没有接收触摸的按钮的直接超级视图?或者我错误地想到了这个?
如果这需要一个冗长的解释,一个好的在线资源会有所帮助 - 除了Apple的UIView文档,我还没有说清楚.
谢谢!
Noa*_*oam 140
我已经修改了接受的答案的代码更通用 - 它处理视图将子视图剪辑到其边界的情况,可能是隐藏的,更重要的是:如果子视图是复杂的视图层次结构,将返回正确的子视图.
- (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;
}
Run Code Online (Sandbox Code Playgroud)
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
}
Run Code Online (Sandbox Code Playgroud)
我希望这有助于任何人尝试将此解决方案用于更复杂的用例.
tob*_*pwn 33
好的,我做了一些挖掘和测试,这里的hitTest:withEvent工作原理 - 至少在高水平.想象这个场景:
图:
+------------------------------+
|E |
| |
| |
| |
| |
|+-----+ |
||B | |
|+-----+ |
|+----------------------------+|
||M ||
|| ||
|+----------------------------+|
+------------------------------+
Run Code Online (Sandbox Code Playgroud)
因为(B)在(M)的框架之外,(B)区域中的抽头将永远不会被发送到(M) - 实际上,(M)在这种情况下从不分析触摸,并且触摸被发送到层次结构中的下一个对象.
但是,如果hitTest:withEvent:在(M)中实现,应用程序中任何位置的点击都将被发送到(M)(或者它至少知道它们).在这种情况下,您可以编写代码来处理触摸并返回应该接收触摸的对象.
更具体地说:目标hitTest:withEvent:是返回应该接收命中的对象.所以,在(M)中你可以写这样的代码:
// 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];
}
Run Code Online (Sandbox Code Playgroud)
我仍然是这个方法和这个问题的新手,所以如果有更有效或正确的方法来编写代码,请评论.
我希望能帮助后来遇到这个问题的其他人.:)
dua*_*uan 24
在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
}
Run Code Online (Sandbox Code Playgroud)