之前有过类似的问题,我提到了Capturing touches on a subview outside the frame of its superview using hitTest:withEvent:和Delivering touch events to a view outside the bounds of its parent view。但他们似乎没有回答我的特殊问题。
我有一个具有以下结构的自定义控件:
+-------------------------------+
| UIButton |
+-------------------------------+
| |
| |
| UITableView |
| |
| |
+------------------------- +
自定义控件会覆盖UIButton
子类,并将UITableView
添加为其子视图。这个想法是让整个控件像下拉列表一样。按UIButton
后,UITableView
将下拉并启用选择。
如果控件是最顶层的hitTest
的直接子项,如上面的Apple的Q& A链接中所述UIButton
覆盖UIView
这一切都正常。但是,如果控件位于另一个视图层次结构中,则UITableView
未接收到触摸事件。
以下是使用的hitTest代码:
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
// Convert the point to the target view's coordinate system.
// The target view isn't necessarily the immediate subview
let pointForTargetView = self.spinnerTableView.convertPoint(point, fromView:self)
if (CGRectContainsPoint(self.spinnerTableView.bounds, pointForTargetView)) {
// The target view may have its view hierarchy,
// so call its hitTest method to return the right hit-test view
return self.spinnerTableView.hitTest(pointForTargetView, withEvent:event);
}
return super.hitTest(point, withEvent: event)
}
修改
我很抱歉没有能够查看答案并检查他们是否解决了问题,因为其他一些任务需要立即引起注意。我会检查它们并接受一个有帮助的。谢谢你的耐心等待。
答案 0 :(得分:5)
正如你所说:
但是,如果控件位于另一个视图层次结构中,则为UITableView 没有收到触摸事件。
即使你让它工作了,如果你在另一个UIView下查看了这个控件?
这样我们就有了转换视图层次结构的点的负担,我的建议是,正如您在this control中看到的那样,它将tableview添加到控件本身所在的同一层次结构中。 (例如,它在添加了此控件的控制器上添加了表视图)
链接中的一部分:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
[self setupSuggestionList];
[suggestionListView setHidden:NO];
// Add list to the super view.
if(self.dataSourceDelegate && [self.dataSourceDelegate isKindOfClass:UIViewController.class])
{
[((UIViewController *)self.dataSourceDelegate).view addSubview:suggestionListView];
}
// Setup list as per the given direction
[self adjustListFrameForDirection:dropDownDirection];
}
希望有所帮助!
答案 1 :(得分:4)
我认为你应该覆盖pointInside
实施:
class Control: UIButton {
var dropdown: Bool = false
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
if dropdown {
return CGRectContainsPoint(CGRect(x: 0, y:0, width: self.bounds.width, self.bounds.height + spinnerTableView.frame.height), point)
}
return super.pointInside(point, withEvent: event)
}
}
像这样,superview呼叫控件的hittest
在触摸控制范围外时,它不会返回nil。
但仍有问题,如果控件位于另一个视图层次结构中,则UITableView可能无法接收触摸事件。
我认为这个问题是正常的,如果控件超级视图hitTest
返回nil,则控件hitTest
方法无法控制其他视图中的pointInside与否不会被叫。
除非你覆盖了所有受控制的pointInside
方法,但这是不现实的。
答案 2 :(得分:3)
我正在考虑UIResponder
,例如:
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
// Convert the point to the target view's coordinate system.
// The target view isn't necessarily the immediate subview
var responder: UIResponder = self
while responder.nextResponder() != nil {
responder = responder.nextResponder()!
if responder is UITableView {
// Got UITableView
break;
}
}
let pointForTargetView = responder.convertPoint(point, fromView:self)
if (CGRectContainsPoint(responder.bounds, pointForTargetView)) {
// The target view may have its view hierarchy,
// so call its hitTest method to return the right hit-test view
return self.responder.hitTest(pointForTargetView, withEvent:event);
}
return super.hitTest(point, withEvent: event)
}
如果此方法不起作用,请尝试convertPoint
使用superview:
当您确定self.spinnerTableView
不是零并且它是您的桌子时,请执行以下操作:
let pointForTargetView = self.spinnerTableView.superView.convertPoint(point, fromView:self)
答案 3 :(得分:1)
你需要subClass UIView,并将它用作你的UIViewController的根视图。
class HitTestBottomView: UIView {
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
if let hitTestView = self.getHitTestButtonFrom(self){
let pointForHitTestView = hitTestView.convertPoint(point, fromView: self)
let pointForHitTestTableView = hitTestView.spinnerTableView.convertPoint(point, fromView: self)
if CGRectContainsPoint(hitTestView.bounds, pointForHitTestView) || CGRectContainsPoint(hitTestView.spinnerTableView.bounds, pointForHitTestTableView){
return hitTestView.hitTest(pointForHitTestView, withEvent: event)
}
}
return super.hitTest(point, withEvent: event)
}
func getHitTestButtonFrom(view:UIView) ->HitTestButton?{
if let testView = view as? HitTestButton {
return testView
}
for subView in view.subviews {
if let testView = self.getHitTestButtonFrom(subView) {
return testView
}
}
return nil
}
}
答案 4 :(得分:1)
你必须实施
func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool
为UIButton
并检查UITableView
边界。由于hitTest
首先会检查此抽头点是否在您的视图范围内,然后为您的视图调用hitTest
。所以你必须实现pointInside
并为你的界限返回true。
WWDC for advanced scroll views and touch handling techniques wwdc