在 Swift 中遍历视图控制器层次结构

the*_*tic 5 generics optional-variables ios swift

我想遍历 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

    }

}
Run Code Online (Sandbox Code Playgroud)

现在,我知道 parentViewController 属性返回一个可选的 UIViewController,但我不知道如何以上帝的名义使 Generic 成为可选类型。也许使用某种 where 子句?

Mar*_*n R 6

您的方法应该返回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
    }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

if let vc = self.traverseAndFindClass() as SpecialViewController? {
    // ....
}
Run Code Online (Sandbox Code Playgroud)

Update: The above method does not work as expected (at least not in the Debug configuration) and I have posted the problem as a separate question: Optional binding succeeds if it shouldn't. One possible workaround (from an answer to that question) seems to be to replace

if let result = parentVC as? T { ...
Run Code Online (Sandbox Code Playgroud)

with

if let result = parentVC as Any as? T { ...
Run Code Online (Sandbox Code Playgroud)

or to remove the type constraint in the method definition:

func traverseAndFindClass<T>() -> T? {
Run Code Online (Sandbox Code Playgroud)

Update 2: The problem has been fixed with Xcode 7, the traverseAndFindClass() method now works correctly.


Swift 4 update:

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
    }
}
Run Code Online (Sandbox Code Playgroud)