extension UIViewController { func traverseAndFindClass<T where T : UIViewController>(T.Type) -> T? { var currentVC = self while let parentVC = currentVC.parentViewController { println("comparing \(parentVC) to \(T.description())") if let result = parentVC as? T { // (XXX) return result } currentVC = parentVC } return nil } }
该方法应遍历父视图控制器层次结构并返回第一个
给定类的实例,如果没有找到则为nil.
但它不起作用,我无法弄清楚原因.可选的绑定
用(XXX)标记的总是成功,以便返回第一个父视图控制器
即使它不是T的实例.
这很容易重现:从“iOS Master-Detail Application”创建项目
Xcode 6 GM中的模板,并将以下代码添加到viewDidLoad()中
MasterViewController类:
if let vc = self.traverseAndFindClass(UICollectionViewController.self) { println("found: \(vc)") } else { println("not found") }
self是一个MasterViewController(UITableViewController的子类),以及它
父视图控制器是一个UINavigationController.
父视图中没有UICollectionViewController
控制器层次结构,所以我期望这个方法
返回nil,输出“未找到”.
但这是发生的事情:
comparing <UINavigationController: 0x7fbc00c4de10> to UICollectionViewController found: <UINavigationController: 0x7fbc00c4de10>
这显然是错误的,因为UINavigationController不是它的子类
UICollectionViewController.也许我犯了一些愚蠢的错误,但我找不到它.
为了隔离问题,我也尝试用自己的类重现它
层次结构,独立于UIKit:
class BaseClass : NSObject { var parentViewController : BaseClass? } class FirstSubClass : BaseClass { } class SecondSubClass : BaseClass { } extension BaseClass { func traverseAndFindClass<T where T : BaseClass>(T.Type) -> T? { var currentVC = self while let parentVC = currentVC.parentViewController { println("comparing \(parentVC) to \(T.description())") if let result = parentVC as? T { // (XXX) return result } currentVC = parentVC } return nil } } let base = BaseClass() base.parentViewController = FirstSubClass() if let result = base.traverseAndFindClass(SecondSubClass.self) { println("found: \(result)") } else { println("not found") }
你猜怎么着?现在它按预期工作!输出是
comparing <MyApp.FirstSubClass: 0x7fff38f78c40> to MyApp.SecondSubClass not found
更新:
func traverseAndFindClass<T>(T.Type) -> T?
正如@POB在评论中所建议的那样,它可以按预期工作.
>通过“两步绑定”替换可选绑定
if let result = parentVC as Any as? T { // (XXX)
正如@vacawama在他的回答中所建议的那样,它也能按预期工作.
>将构建配置从“Debug”更改为“Release”也会使
方法按预期工作. (到目前为止,我只在iOS模拟器中对此进行了测试.)
最后一点可能表明这是一个Swift编译器或运行时错误.我还是
无法理解UIViewController的子类出现问题的原因,但不是
与我的BaseClass的子类.因此,我会将问题保持开放
而在接受答案之前.
更新2:从Xcode 7开始修复此问题.
随着最终的Xcode 7
释放问题不再发生.可选的绑定
如果让result = parentVC为? T在traverseAndFindClass()方法中
现在可以在Release和Debug配置中按预期工作(并失败).
解决方法
var nc = UINavigationController() if let vc = nc as? UICollectionViewController { println("Yes") } else { println("No") }
你收到这个错误:
Playground执行失败:: 33:16:错误:’UICollectionViewController’不是’UINavigationController’的子类型
如果让vc = nc为? UICollectionViewController {
但如果相反,你做:
var nc = UINavigationController() if let vc = (nc as Any) as? UICollectionViewController { println("Yes") } else { println("No") }
它打印“否”.
所以我建议尝试:
extension UIViewController { func traverseAndFindClass<T where T : UIViewController>(T.Type) -> T? { var currentVC = self while let parentVC = currentVC.parentViewController { println("comparing \(parentVC) to \(T.description())") if let result = (parentVC as Any) as? T { // (XXX) return result } currentVC = parentVC } return nil } }