如何创建一个序列来迭代从 UIView 向上到视图层次结构到根的所有祖先(超级视图)?

Posted

技术标签:

【中文标题】如何创建一个序列来迭代从 UIView 向上到视图层次结构到根的所有祖先(超级视图)?【英文标题】:How can I make a Sequence to iterate over all the ancestors (superviews) from a UIView up the view hierarchy to the root? 【发布时间】:2017-03-23 23:18:41 【问题描述】:

我希望UIView 有一个属性,该属性返回视图的所有祖先的序列。这对于查找与特定类型匹配的最近的目的很有用:

let tableView = cell.ancestors.first(where:  $0 is UITableView )

实现ancestors 属性的好方法是什么?

【问题讨论】:

【参考方案1】:

使用 Swift 标准库 中的 sequence(first:next:) 函数,也可以使用更短的解决方案:

extension UIView 
    var ancestors: AnySequence<UIView> 
        return AnySequence<UIView>(
            sequence(first: self, next:  $0.superview ).dropFirst())
    

【讨论】:

【参考方案2】:

您可以实现一个符合Sequence 的类型,并在扩展中添加一个返回它的属性。 Sequence 通常需要一个 makeIterator() 方法来返回符合 IteratorProtocol 的类型,但在这种情况下,我们可以让序列充当它自己的迭代器并为两者使用一种类型,这使得事情变得非常简单:

斯威夫特 3:

struct AncestorSequenceIterator: Sequence, IteratorProtocol 
    var current: UIView
    mutating func next() -> UIView? 
        guard let next = current.superview else  return nil 
        current = next
        return next
    


extension UIView 
    var ancestors: AncestorSequenceIterator 
        return AncestorSequenceIterator(current: self)
    

【讨论】:

如果ancestors 属性的唯一用途是查找特定类型的父级,那么您的解决方案效率低下。您构建了整个祖先列表,但实际上只迭代前几个(通常)以找到匹配的祖先。为什么要先遍历整个 superview 列表,然后返回并仅迭代前几个以找到匹配项?为什么不消除不必要的第一步,而只做更短(通常)的第二步?可以通过对 UIView 的简单扩展来完成。您的 ancestors 属性可能有其用途,但此用途不是其中之一。 @rmaddy 这并没有构建一个完整的祖先列表——它只是创建了一个可以直接向上迭代视图层次结构的迭代器。使用first(where:) 将在找到满足谓词的第一个匹配项时短路。如果你想建立一个祖先列表,你必须说类似Array(someView.ancestors) @Hamish 哦,好点。这否定了我的全部评论。没关系,继续前进。这没东西看。 :) 谢谢。【参考方案3】:

您可以创建扩展并返回 IteratorProtocol 以便能够像这样进行 first(where:) 比较,

extension UIView 

    var ancestors: AnyIterator<UIView> 
        var current: UIView = self

        return AnyIterator<UIView> 
            guard let parent = current.superview else 
                return nil
            
            current = parent
            return parent

        
    

由于 AnyIterator 本身符合 Sequence,因此您上面显示的语句应该可以正常工作。

let tableView = cell.ancestors.first(where:  $0 is UITableView )

【讨论】:

【参考方案4】:

Paulo Mattos 的实现很好,但对于您的特定用途,您可能想要这样的东西:

extension UIView 
    func nearestAncestor<T: UIView>(ofType type: T.Type) -> T? 
        if let me = self as? T  return me 
        return superview?.nearestAncestor(ofType: type)
    

那么你可以这样使用它:

guard let tableView = cell.nearestAncestor(ofType: UITableView.self) else  return 
// tableView at this point is type UITableView

【讨论】:

这是解决特定问题的好方法,但在这种情况下,我想看看是否可以以更通用和更实用的方式解决它:)

以上是关于如何创建一个序列来迭代从 UIView 向上到视图层次结构到根的所有祖先(超级视图)?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 UIView 中设置滚动视图以向上滑动被键盘隐藏的文本字段?

UIView 的动画透视图从底部向上/远离底部翻转

如何旋转两个 UIView?

自定义按钮视图:检测触摸向下/向上,快速,并回退拖动到 UIScrollView

如何使 UIView 充当操作表?

创建一个基于 UIView 的子视图自动调整大小的视图