swift 接收边界外的触摸事件

fcg9iug3  于 2023-11-16  发布在  Swift
关注(0)|答案(6)|浏览(118)

以前也有类似的问题,我提到了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将按钮并启用选择选项。
如果控件是最顶层UIView的直接子控件,则这一切都可以在UIButton中覆盖,如上面Apple的Q&A链接中所述。但是,如果控件位于另一个视图层次结构中,则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)
}

编辑:

我很抱歉没有能够看到答案,并检查他们是否解决了问题,由于一些其他的任务,需要立即注意.我会检查他们,并接受一个有帮助.感谢您的耐心.

3phpmpom

3phpmpom1#

正如你所说:
但是,如果控件位于另一个视图层次结构中,则可扩展视图不会接收触摸事件。
即使你让它工作,如果你在另一个UIView下子视图这个控件呢??
这样我们就有了为视图层次结构转换点的负担,我的建议是,正如你在this control中看到的那样,它将tableview添加到控件本身所在的同一层次结构上。(例如,它将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];
}

字符串
希望有帮助!

w46czmvw

w46czmvw2#

我认为你应该重写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。
但是仍然有一个问题,如果控件在另一个视图层次结构中,则可扩展视图可能不会接收触摸事件。
我觉得这个问题很正常,你不能决定其他视图的pointInside是否在哪个控件之下,如果控件superview hitTest返回nil,控件hitTest的方法就不会被调用。除非你覆盖了控件下所有的view pointInside方法,但这是不现实的。

vkc1a9a2

vkc1a9a23#

我在想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)
}

字符串
如果此方法不起作用,请尝试使用superview convertPoint
当你确定self.spinnerTableView不为nil并且它是你的表时,做:

let pointForTargetView = self.spinnerTableView.superView.convertPoint(point, fromView:self)

nom7f22z

nom7f22z4#

您需要子类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
  }
}

字符串

7vux5j2d

7vux5j2d5#

你必须执行

func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool

字符串
为你的UIButton并检查UITableView边界。因为hitTest首先检查这个点击点是否在你的视图的边界内,然后为你的视图调用hitTest。所以你必须实现pointInside并为你的边界返回true。
WWDC for advanced scroll views and touch handling techniques wwdc

pkmbmrz7

pkmbmrz76#

解决方案Swift 5.2

你需要在UIView或按钮的超级视图中覆盖它。如果UIView是V,并且你将UIView V1或按钮B1放在V中,你需要将代码放在UIView V中。

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
   }

字符串

相关问题