如何创建一个序列来迭代从 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 中设置滚动视图以向上滑动被键盘隐藏的文本字段?