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

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

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

来自 UI 层次结构的片段:

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

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

【问题讨论】:

  • 如果你正在寻找 UIViews 那么你应该用 UIKit 来标记它,而不是 SpriteKit

标签: ios swift uikit


【解决方案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】:

这可以通过两步来实现:

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

    func descendants(of view: UIView) -> [UIView] {
        return view.subviews + view.subviews.flatMap(descendants(of:))
    }
    
  2. 过滤包含接触点的视图:

    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() 可能会返回视图本身。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-21
        相关资源
        最近更新 更多