如何在其父视图范围之外启用视图的触摸?

Posted

技术标签:

【中文标题】如何在其父视图范围之外启用视图的触摸?【英文标题】:How to enable touches of a view outside bounds of its superview? 【发布时间】:2018-06-12 12:40:39 【问题描述】:

我有一个将clipToBounds 设置为false 的容器和一个超出其边界的视图。越界视图无法识别触摸事件。

【问题讨论】:

【参考方案1】:

只需将此类添加到您的视图中

class MyView: UIView 

    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool 
        for subview in subviews as [UIView] 
            if !subview.isHidden 
               && subview.alpha > 0 
               && subview.isUserInteractionEnabled 
               && subview.point(inside: convert(point, to: subview), with: event) 
                 return true
            
        
        return false
    

【讨论】:

谢谢,但我正在寻找不涉及子类化的解决方案,因为我有一个可重用的视图,该视图将具有超出范围的组件。对每个视图控制器的每个视图进行子类化是一种错误的方法,我想在其中添加包含越界组件的视图。【参考方案2】:

这是一个扩展,使您能够允许触摸容器中的剪辑子视图。将此文件粘贴到您的项目中并设置containerView.allowTouchesOfViewsOutsideBounds = true

public extension UIView 

    private struct ExtendedTouchAssociatedKey 
        static var outsideOfBounds = "viewExtensionAllowTouchesOutsideOfBounds"
    

    /// This propery is set on the parent of the view that first clips the content you want to be touchable
    /// outside of the bounds
    var allowTouchesOfViewsOutsideBounds:Bool 
        get 
            return objc_getAssociatedObject(self, &ExtendedTouchAssociatedKey.outsideOfBounds) as? Bool ?? false
        
        set 
            UIView.swizzlePointInsideIfNeeded()
            subviews.forEach($0.allowTouchesOfViewsOutsideBounds = newValue)
            objc_setAssociatedObject(self, &ExtendedTouchAssociatedKey.outsideOfBounds, newValue, .OBJC_ASSOCIATION_RETAIN)
        
    

    func hasSubview(at point:CGPoint) -> Bool 

        if subviews.count == 0 
            return self.bounds.contains(point)
        
        return subviews.contains(where:  (subview) -> Bool in
            let converted = self.convert(point, to: subview)
            return subview.hasSubview(at: converted)
        )

    

    static private var swizzledMethods:Bool = false

    @objc func _point(inside point: CGPoint, with event: UIEvent?) -> Bool 

        if allowTouchesOfViewsOutsideBounds 
            return  _point(inside:point,with:event) || hasSubview(at: point)
        
        return _point(inside:point,with:event)
    

    static private func swizzlePointInsideIfNeeded() 
        if swizzledMethods 
            return
        
        swizzledMethods = true
        let aClass: AnyClass! = UIView.self
        let originalSelector = #selector(point(inside:with:))
        let swizzledSelector = #selector(_point(inside:with:))
        swizzle(forClass: aClass, originalSelector: originalSelector, swizzledSelector: swizzledSelector)
    

【讨论】:

swizzle(forClass: aClass, originalSelector: originalSelector, swizzledSelector: swizzledSelector) 这行不行。 不编译。什么是“调酒”?

以上是关于如何在其父视图范围之外启用视图的触摸?的主要内容,如果未能解决你的问题,请参考以下文章

如何将子视图上的触摸事件的坐标转换为其父视图中的坐标?

如何检测拖动到那里的子视图中的触摸(或获取哪个子视图位于其父视图中的点击下方的最快方法)

UIView clipToBounds 没有停止子视图接收父视图之外的触摸

如何在其父视图坐标系中获得子视图的有效边界

检测呈现视图之外的触摸以将其关闭

UITableView - 如何将触摸传递给父视图