这是我在Swift(略作修改)中发布的遍历视图控制器层次结构的可能解决方案:
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。
(XXX)
T
这很容易被复制:创建一个从在Xcode6通用的“iOS的主从应用程序”模板的项目,下面的代码添加到viewDidLoad()该的MasterViewController类:
viewDidLoad()
MasterViewController
if let vc = self.traverseAndFindClass(UICollectionViewController.self) { println("found: \(vc)") } else { println("not found") }
self是MasterViewController(的子类UITableViewController),并且其父视图控制器是UINavigationController。UICollectionViewController父视图控制器层次结构中没有该类,因此我希望该方法返回nil并且输出“未找到”。
self
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在他的回答中所建议的,它也可以按预期工作。
最后一点可能表明这是Swift编译器或运行时错误。而且我仍然无法理解为什么问题出现在的子类中UIViewController,而不是出现在我的子类中BaseClass。因此,在接受答案之前,我将让问题开放一段时间。
UIViewController
BaseClass
更新2: 自 Xcode 7起, 此问题已修复。
在最终的Xcode 7版本中,该问题不再发生。现在,在Release和Debug配置中if let result = parentVC as? T,该traverseAndFindClass()方法中的可选绑定 均按预期工作(并且失败)。
if let result = parentVC as? T
traverseAndFindClass()
如果您尝试在Playground中将类型UINavigationController为object的对象UICollectionViewController强制转换为:
var nc = UINavigationController() if let vc = nc as? UICollectionViewController { println("Yes") } else { println("No") }
您收到此错误:
游乐场执行失败:: 33:16:错误:如果让vc = nc为“ UICollectionViewController”不是“ UINavigationController”的子类型? 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 } }