2016-01-29 10 views
6

UIViewControllerクラスまたはサブクラスTypeを渡してviewControllersのスタックをソートする一連の汎用関数を記述しようとしています。 「見つかった」viewControllerまたはnil。Swift Generics:T.Typeをパラメータとして持つ関数は、オプションで返されます。T

extension UINavigationController { 

    func fhk_find<T: UIViewController>(viewControllerType: T.Type) -> T? 
    { 
     if let viewController = viewControllers.first as? viewControllerType { 
      return viewController 
     } 
     else { 
      return nil 
     } 
    } 
} 

をして呼び出します。:これまでのところ私もコンパイルするために、この簡単なスニペットを取得することができなかった

navController.fhk_find(fooViewController.self) 

しかし、コンパイラはviewControllerTypeがタイプではないことを私に語っています。

私は私がここに欠けているかを正確にわからないんだけど...

答えて

4

あなたはTにではなく、パラメータviewControllerTypeに(場合には、それが存在する)viewControllers.firstをキャストする必要があります。実際には、このパラメータをまったく使用する必要はありません。拡張機能を次のように変更することができます。

extension UINavigationController { 
    func fhkFindFirst<T: UIViewController>(_: T.Type) -> T? { 
     for viewController in viewControllers { 
      if let viewController = viewController as? T { 
       return viewController 
      } 
     } 
     return nil 
    } 
} 

使用例は以下のとおりです。 fooViewController.selfではなくfooViewController.dynamicTypeで電話することに注意してください。後者の場合は、fooViewController.self = fooViewController.selfではなくの値fooViewController.selfになります。

ただし、サブクラスタイプからそのスーパークラスへの変換を試みると常に成功するので、上記の解決策ではサブクラスインスタンス(サブクラスはUIViewController)を正しく識別します。一方、スーパークラスインスタンス(つまり、 TをスーパークラスUIViewControllerとした場合)viewController = viewController as? Tは、viewControllerが実際にUIViewControllerのサブクラスのインスタンスであっても、常に成功します。

サブクラスのインスタンスを識別するために fhkFindFirst(...)を使用し

:OK

次の例では、2つのサブクラスのインスタンスが正しく識別し、その参照は、呼び出し元に返されます。

class FooViewController : UIViewController { } 
class BarViewController : UIViewController { } 

let fooViewController = FooViewController() 
let barViewController = BarViewController() 

let navController = UINavigationController(rootViewController: fooViewController) 
navController.addChildViewController(barViewController) 

print(navController.fhkFindFirst(fooViewController.dynamicType) ?? "None found.") 
// or: print(navController.fhkFindFirst(FooViewController)) 
/* <__lldb_expr_1582.FooViewController: 0x7fb97840ad80> */ 

print(navController.fhkFindFirst(barViewController.dynamicType) ?? "None found.") 
// or: print(navController.fhkFindFirst(BarViewController)) 
/* <__lldb_expr_1582.BarViewController: 0x7fb978709340> */ 

スーパークラスのインスタンスを見つけるために、fhkFindFirst(...)を使用した:

を意図していないとして、次の場合には、fooViewControllerUIViewControllerオブジェクトであり、型変換ので、BarViewControllerとして誤識別されるのに対しBarViewControllerオブジェクトの(UINavigationController.viewControllers)〜UIViewControllerが成功しました。

class BarViewController : UIViewController { } 

let fooViewController = UIViewController() 
let barViewController = BarViewController() 

let navController = UINavigationController(rootViewController: barViewController) 
navController.addChildViewController(fooViewController) 

print(navController.fhkFindFirst(fooViewController.dynamicType) ?? "None found.") 
// or: print(navController.fhkFindFirst(UIViewController)) 
    /* <__lldb_expr_1533.BarViewController: 0x7fa519e2bd40> <-- "wrong" one */ 

print(navController.fhkFindFirst(barViewController.dynamicType) ?? "None found.") 
// or: print(navController.fhkFindFirst(BarViewController)) 
    /* <__lldb_expr_1533.BarViewController: 0x7fa519e2bd40> */ 

アップラップします。 UIViewControllerのサブクラスを検索するためのメソッドのみを使用する限り、上記は機能します。一方、スーパークラスオブジェクトを検索しようとすると、最初のコントローラはUINavigationController.viewControllersになります。


edelaney05に関して追加:

edelaney05:コメントを削除するた場合には、私は疑問を引用することから始めましょう

下のコメント欄での関連する質問:これを防ぐ方法はありますか?これは特にあなたが 中級クラスを持っている場合に考慮すべき非常に興味深いケースです。 class FooVC: AwesomeVC { ... },class BarVC: AwesomeVC { ... }、およびclass AwesomeVC: UIViewController { ... }

はい、あなたはあなたがUIViewControllerのサブクラスのみの純粋な(第一レベル)以外で作業することがあります知っている場合には、この現象を回避することができます。例えば、

  • サブクラスからUIViewControllerの最初のインスタンスを見つけることができます。 (+)
  • これらのサブクラスのサブクラスです。 (++)

サブクラスインスタンスをそのスーパークラスに変換しないようにするために、動的型比較を使用することができます。したがって、サブクラスインスタンスを私たちが探しているスーパークラスインスタンスとして誤って識別することができますために)。

extension UINavigationController { 
    func fhkFindFirst<T: UIViewController>(_: T.Type) -> T? { 
     for viewController in viewControllers { 
      if let supClassType = viewController.superclass?.dynamicType where supClassType != T.Type.self { 
       if let viewController = viewController as? T { 
        return viewController 
       } 
      } 
     } 
     return nil 
    } 
} 

class FooBarViewController: UIViewController { } 

class FooViewController : FooBarViewController { } 
class BarViewController : FooBarViewController { } 

let fooBarViewController = FooBarViewController() 
let fooViewController = FooViewController() 
let barViewController = BarViewController() 

let navController = UINavigationController(rootViewController: fooViewController) 
navController.addChildViewController(barViewController) 
navController.addChildViewController(fooBarViewController) 

print(navController.fhkFindFirst(FooViewController) ?? "None found.") 
/* <__lldb_expr_1582.FooViewController: 0x7fe22a712e40> */ 

print(navController.fhkFindFirst(BarViewController) ?? "None found.") 
/* <__lldb_expr_1582.BarViewController: 0x7fe22a4196a0> */ 

print(navController.fhkFindFirst(FooBarViewController) ?? "None found.") 
/* <__lldb_expr_1582.FooBarViewController: 0x7fe22a70ee60> */ 
+0

Ah!私はそれが何か簡単だと分かっていました。ありがとうございました! .selfと.dynamicTypeを指摘して、将来の頭痛を救ってくれてありがとう。 –

+0

@StephenNewtonお手伝いします。またメソッド名にSwift 'camelCase'命名規則に従うように拡張メソッドを更新しました。 – dfri

+2

@StephenNewton私の編集に注意してください:上のソリューションは、UIViewController'のサブクラスインスタンスのみを検索する限り正常ですが、スーパークラスオブジェクトを検索しようとすると、常に 'UINavigationController.viewControllers'の最初のインスタンスを返します(' UIViewController ')。しかし、すべてのビューコントローラが 'UIViewController'のサブクラスのインスタンスでなければならないので、これはOKです。 – dfri

関連する問題