Как я могу сделать последовательность для итерации над всеми предками (супервидами) из UIView по иерархии представлений в корень?

Я хотел бы, чтобы UIView имел свойство, которое возвращает последовательность всех предков представления вверх по иерархии. Это было бы полезно для таких целей, как поиск ближайшего, который соответствует определенному типу:

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

Какой хороший способ реализовать свойство ancestors ?

Используя sequence(first:next:) функция, из стандартной библиотеки Swift , возможно еще более короткое решение:

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

Вы можете реализовать тип, соответствующий Sequence и добавить свойство, возвращающее его в расширение. makeIterator() Sequence требуется метод makeIterator() который возвращает тип, соответствующий IteratorProtocol , но в этом случае мы можем заставить последовательность действовать как свой собственный итератор и использовать один тип для обоих, что делает вещи очень простыми:

Swift 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) } } 

Вы могли бы создать расширение и вернуть IteratorProtocol, чтобы иметь возможность сделать сначала (где 🙂 сравнение, например,

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

Реализация Пауло Маттоса хороша, но для вашего конкретного использования вы, вероятно, хотите что-то вроде этого:

 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