All Articles

Swift5.1時代のChildViewのおさらい

前置き

iPad で分割した画面の実装を検討していました。 当初は、UISplitViewController(公式はこちらを参照)で実装を検討していましたが、PrimaryView(メニュー部分)を右側に持って来ることが出来ないため、昔からある ChildViewController で実装する方針に変更しました。

Swift5.1 での ChildViewController 実装について改めて調査したので Blog にまとめていきます。

実装方法

実装の仕方は、2 つに分けられます。

  • Storyboard の ContainerView + Segue での実装
  • ソースコードでの実装

それぞれ見ていきます。

Storyboard の ContainerView + Segue での実装

StackView の中に、ContainerView を追加

childview storyboard1

各 ContainerView と追加したい ViewController を Segue で連結

childview storyboard2

ChildViewController を add する View を用意しているだけで特に特別なコードは不要。

class ContainerViewController: UIViewController {

    @IBOutlet private weak var listView: UIView!

    @IBOutlet private weak var detailView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

}

ChildView が動的に変わらないのであれば Storyboard で実装するのは楽ちんです。

ソースコードでの実装

Storyboard としては ContainerView の代わりに View を利用。 Segue は特に貼っていないので、各 ViewController は完全に独立しています。

childview source1

childview source2

ソース全文は下記の通りです。

class ContainerViewController: UIViewController {

    @IBOutlet private weak var listView: UIView! {
        didSet {
            add(instance("list"), containerView: listView)
        }
    }
    @IBOutlet private weak var detailView: UIView! {
        didSet {
            add(instance("detail"), containerView: detailView)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }

}

extension UIViewController {
    func add(_ child: UIViewController, containerView: UIView, frame: CGRect? = nil) {
        addChild(child)
        if let frame = frame {
            child.view.frame = frame
        }
        containerView.addSubview(child.view)
        child.didMove(toParent: self)
    }

    func remove(containerView: UIView) {
        willMove(toParent: nil)
        containerView.removeFromSuperview()
        removeFromParent()
    }
}

extension UIViewController {
    func instance(_ identifier: String) -> UIViewController {
         UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: identifier)
    }
}

View の追加だと、 listView.addSubview(<#T##view: UIView##UIView#>) のように書けますが、 ChildViewController の追加の場合は、それほど単純ではありません。

How to use view controller containment より引用させてもらうと、以下のような 4 ステップを行わないといけません。

  1. Call addChild() on your parent view controller, passing in your child.
  2. Set the child’s frame to whatever you need, if you’re using frames.
  3. Add the child’s view to your main view, along with any Auto Layout constraints.
  4. Call didMove(toParent:) on the child, passing in your main view controller.

実際に利用するなら、UIViewController の Extension を作成する形になるかと思います。

追加する ChildView を条件により変更しない場合は、Storyboard を活用した ContainerView + Segue の方が断然楽でしょう。

参考