LoginSignup
11
11

More than 5 years have passed since last update.

深い階層の UIView が 特定の UIViewController を見つける方法

Last updated at Posted at 2016-03-30

ものすごく深い階層の View から 特定のViewController にたどり着きたい時があったとします。そのView から その ViewController にたどり着くには、中間に UIView や UIViewController がいくつも介在している可能性もあります。 NSNotification を投げてやりとりをする人もいるかもしれませんが、今回は別の方法を紹介したいと思います。

Screen Shot 2016-03-31 at 12.27.55 AM.png

iOS も OS X もイベントなどを処理する仕組みとして Responder Chain と言う仕組みがあります。これを辿れば、目的となる View Controller が見つかるはずです。

OS X では、NSViewController が Responder Chain に入ってきたのは最近ですので、version の確認が必要です。WWDC 2104 Storyboards and Controllers on OS X

そこで、こんな Extension を紹介いたします。

extension UIResponder {

    func findViewController<T: UIViewController>() -> T? {
        var responder = self.nextResponder()
        while responder != nil {
            if let viewController = responder as? T {
                return viewController
            }
            responder = responder!.nextResponder()
        }
        return nil
    }

}

EDIT: UIResponder の extension に変更しました。2016.5.19

使い方は以下のようなイメージです。 Generics で UIViewController の縛りが入っているので、as? MyViewController とか as? UIViewController とか型を決定させてあげる必要があります。この場合当然見つからない場合は nil が帰ってきます。

let myViewController = view.findViewController() as? MyViewController

追記: うまく型が Generic に伝わらない場合がある事に気がつきました。まだ挙動に関して納得できていない部分が多いので、詳細は調査中としますが、以下のコードの方が安全のようです。2016.5.19

if let myViewController: MyViewController = view.findViewController() {
    //...
}

同様に、superview を辿って、特定のクラスにたどり着きたい場合には、以下の extension を使います。

extension UIView {

    func findView<T: UIView>() -> T? {
        var view = self.superview
        while view != nil {
            if let view = view as? T {
                return view
            }
            view = view!.superview
        }
        return nil
    }

}

使い方の例は、以下の通りです。

let myView = view.findView() as? MyView

View が多重階層となり、View と ViewController にいくつもの 中間 View や 中間 ViewController を介在するようになると、多くの中間 delegate プロトコルを用意して、処理やイベントを中継に中継を重ねて伝達させる必要があったりします。そんな場合には、こんな方法もありますが、無用な密結合が生まれるのでご注意ください。

応用すれば、Responder Chain で特定の Delegate などの特定のプロトコルを持つオブジェクトを探す、なんてのも可能かと思いますので、機会があれば是非実験してみてください。では、Have a happy coding!

swift --version
Apple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29)
11
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
11