如何在我的触摸位置获取层次结构中的所有视图 - Swift

Posted

技术标签:

【中文标题】如何在我的触摸位置获取层次结构中的所有视图 - Swift【英文标题】:How to get all views in hierarchy in my touch position - Swift 【发布时间】:2018-03-05 13:56:27 【问题描述】:

如何在我的触摸位置获取所有 UIView?

在下图中,我需要让所有 UIView 都与黄线接触。

来自 UI 层次结构的片段:

我正在寻找与 SpriteKit 类似的功能,我们用它来获取所有节点

self.nodes(at: touch.location(in: self))

【问题讨论】:

如果你正在寻找 UIViews 那么你应该用 UIKit 来标记它,而不是 SpriteKit 【参考方案1】:

您必须遍历层次结构中的所有视图,例如:

extension UIView 
    func allViews(for touch: UITouch) -> [UIView] 
        return self.allViews(at: touch.location(in: self))
    

    func allViews(at point: CGPoint) -> [UIView] 
        var stack = [UIView]()
        var result = [UIView]()

        stack.append(self)
        while let view = stack.popLast() 
            let localPoint = view.convert(point, from: self)

            if view.bounds.contains(localPoint) 
                result.append(view)
            
            stack.append(contentsOf: view.subviews)
        
        return result
    

您可以从包含所有适当视图(例如视图控制器的视图)或窗口的任何超级视图开始。第二种方法的点必须相对于由self 表示的视图边界。

【讨论】:

为什么 break 语句意味着永远不会发生? @Saranjith:只有在堆栈不为空时才会进入while循环。因此,stack.popLast() 应该总是返回一个视图。 @clemens 然后强制解开stack.popLast() 或使用while let view = stack.popLast() 开始。 @ChristianSchnorr:谢谢,很好的提示。我已经修改了我的帖子。【参考方案2】:

这可以通过两步来实现:

    获取从某个视图开始的所有视图

    func descendants(of view: UIView) -> [UIView] 
        return view.subviews + view.subviews.flatMap(descendants(of:))
    
    

    过滤包含接触点的视图:

    let touchLocation = touchGesture.location(in: nil)
    let matchingViews = descendants(of: UIApplication.shared.keyWindow!)
        .filter  $0.convert($0.bounds, to: nil).contains(touchLocation) 
    

【讨论】:

【参考方案3】:

在 UIKit 中,响应者链的工作方式类似于 this,我认为没有办法获取所有列表,因为它会“继续”直到响应者捕获它而不是进一步。

【讨论】:

【参考方案4】:
func subviewsThatContain(point: CGPoint, in view: UIView) -> [UIView] 
    var views = [UIView]()

    for subview in view.subviews 
        if subview.frame.contains(subview.convert(point, from: self.view)) 
            views.append(subview)
        

        views += subviewsThatContain(point: point, in: subview)
    

    return views

函数调用看起来像这样

var allViews = [self.view] + subviewsThatContain(in: self.view, point: touch.location(in: self.view))

【讨论】:

【参考方案5】:

您可以使用 hittest 检查所有视图层次结构

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? 
        guard let point = targetPoint,
            var view = super.hitTest(point, with: event) else 
                return
        
        var views: [UIView] = [view]
        while view != nil 
            view = view.hitTest(point, with: event)
            if view != nil 
                views.append(view!)
            
        
    

【讨论】:

这并不适用于所有情况,因为hitTest() 需要一个相对于视图边界的点。它不会找到被另一个视图覆盖的视图。它也可能包含一个无限循环,因为hitTest() 可能会返回视图本身。

以上是关于如何在我的触摸位置获取层次结构中的所有视图 - Swift的主要内容,如果未能解决你的问题,请参考以下文章

iOS CALayer.zPosition 不会超越视图层次结构吗?

将 UIView 添加为子视图并在 Swift 中确定它们在视图层次结构中的位置

获取 uiview 层次结构顶部的对象

如何检查 iOS 中的视图层次结构?

如何让jsp中的spring安全角色层次结构起作用?

Android中的错误“只有创建视图层次结构的原始线程才能触摸其视图”[重复]