我想在Swift中遍历视图控制器层次结构并找到一个特定的类。这是代码:
extension UIViewController{
func traverseAndFindClass<T : UIViewController>() -> UIViewController?{
var parentController = self.parentViewController as? T?
^
|
|
// Error: Could not find a user-defined conversion from type 'UIViewController?' to type 'UIViewController'
while(parentController != nil){
parentController = parentController!.parentViewController
}
return parentController
}
}
现在,我知道parentViewController属性返回一个可选的UIViewController,但我不知道如何以God的名义将Generic作为可选类型。也许使用某种where子句?
答案 0 :(得分:2)
您的方法应该返回T?
而不是UIViewController?
,以便通用类型
可以从上下文中推断出来。检查想要的课程也必须
在循环内完成,不仅在循环之前完成一次。
这应该有效:
extension UIViewController{
func traverseAndFindClass<T : UIViewController>() -> T? {
var currentVC = self
while let parentVC = currentVC.parentViewController {
if let result = parentVC as? T {
return result
}
currentVC = parentVC
}
return nil
}
}
使用示例:
if let vc = self.traverseAndFindClass() as SpecialViewController? {
// ....
}
更新:上述方法无法正常工作(至少在Debug中没有 配置),我已经发布了问题 作为一个单独的问题:Optional binding succeeds if it shouldn't。一种可能的解决方法(从该问题的答案) 似乎是要取代
if let result = parentVC as? T { ...
与
if let result = parentVC as Any as? T { ...
或删除方法定义中的类型约束:
func traverseAndFindClass<T>() -> T? {
更新2: Xcode 7解决了这个问题
traverseAndFindClass()
方法现在可以正常使用。
Swift 4更新:
extension UIViewController{
func traverseAndFindClass<T : UIViewController>() -> T? {
var currentVC = self
while let parentVC = currentVC.parent {
if let result = parentVC as? T {
return result
}
currentVC = parentVC
}
return nil
}
}
答案 1 :(得分:1)
一个线性解决方案(使用递归), Swift 4.1 + :
extension UIViewController {
func findParentController<T: UIViewController>() -> T? {
return self is T ? self as? T : self.parent?.findParentController() as T?
}
}
使用示例:
if let vc = self.findParentController() as SpecialViewController? {
// ....
}
答案 2 :(得分:0)
我们可以使用递归代替while
循环(请注意,以下代码均未经过全面测试):
// Swift 2.3
public extension UIViewController {
public var topViewController: UIViewController {
let o = topPresentedViewController
return o.childViewControllers.last?.topViewController ?? o
}
public var topPresentedViewController: UIViewController {
return presentedViewController?.topPresentedViewController ?? self
}
}
关于遍历视图控制器层次结构的更一般问题,一种可能的方法是拥有两个专用序列,以便我们可以:
for ancestor in vc.ancestors {
//...
}
或:
for descendant in vc.descendants {
//...
}
其中:
public extension UIViewController {
public var ancestors: UIViewControllerAncestors {
return UIViewControllerAncestors(of: self)
}
public var descendants: UIViewControllerDescendants {
return UIViewControllerDescendants(of: self)
}
}
实施祖先序列:
public struct UIViewControllerAncestors: GeneratorType, SequenceType {
private weak var vc: UIViewController?
public mutating func next() -> UIViewController? {
guard let vc = vc?.parentViewController ?? vc?.presentingViewController else {
return nil
}
self.vc = vc
return vc
}
public init(of vc: UIViewController) {
self.vc = vc
}
}
实施后代序列:
public struct UIViewControllerDescendants: GeneratorType, SequenceType {
private weak var root: UIViewController?
private var index = -1
private var nextDescendant: (() -> UIViewController?)? // TODO: `Descendants?` when Swift allows recursive type definitions
public mutating func next() -> UIViewController? {
if let vc = nextDescendant?() {
return vc
}
guard let root = root else {
return nil
}
while index < root.childViewControllers.endIndex - 1 {
index += 1
let vc = root.childViewControllers[index]
var descendants = vc.descendants
nextDescendant = { return descendants.next() }
return vc
}
guard let vc = root.presentedViewController where root === vc.presentingViewController else {
return nil
}
self.root = nil
var descendants = vc.descendants
nextDescendant = { return descendants.next() }
return vc
}
public init(of vc: UIViewController) {
root = vc
}
}