ios 如何在视图的超视图边界之外启用视图的触摸?

4sup72z8  于 2023-03-05  发布在  iOS
关注(0)|答案(3)|浏览(132)

我有一个容器,它的clipToBounds设置为false,并且有一个视图超出了它的边界。触摸事件无法识别超出边界的视图。

z3yyvxxp

z3yyvxxp1#

只需将该类添加到视图中

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
    }
}
n7taea2i

n7taea2i2#

这里我修正了之前答案的格式和混合方法

extension UIView {

    private enum 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 {
            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.isEmpty {
            return bounds.contains(point)
        }
        return subviews.contains { subview in
            let converted = self.convert(point, to: subview)
            return subview.hasSubview(at: converted)
        }
    }

    private static var swizzledMethods = false

    @objc
    func _point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        if allowTouchesOfViewsOutsideBounds {
            return originalPoint(inside: point, with: event) || hasSubview(at: point)
        }
        return originalPoint(inside: point, with: event)
    }

    @objc
    func originalPoint(inside point: CGPoint, with event: UIEvent?) -> Bool {
        fatalError("this should be swizzled")
    }

    private static func swizzlePointInsideIfNeeded() {
        if swizzledMethods {
            return
        }
        
        swizzledMethods = true
        
        let aClass = UIView.self
        
        let originalSelector = class_getInstanceMethod(
            aClass,
            #selector(point(inside:with:))
        )!
        
        let swizzledSelector = class_getInstanceMethod(
            aClass,
            #selector(_point(inside:with:))
        )!
        
        let backupSelector = class_getInstanceMethod(
            aClass,
            #selector(originalPoint(inside:with:))
        )!
        
        let nativeImplementation = method_getImplementation(originalSelector)
        let customImplementation = method_getImplementation(swizzledSelector)
        
        method_setImplementation(originalSelector, customImplementation)
        method_setImplementation(backupSelector, nativeImplementation)
    }
}
rjjhvcjd

rjjhvcjd3#

这是一个扩展,允许你在一个容器中点击剪切的子视图。将这个文件粘贴到你的项目中并设置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)
    }
}

相关问题