前置き
iPad で分割した画面の実装を検討していました。 当初は、UISplitViewController(公式はこちらを参照)で実装を検討していましたが、PrimaryView(メニュー部分)を右側に持って来ることが出来ないため、昔からある ChildViewController で実装する方針に変更しました。
Swift5.1 での ChildViewController 実装について改めて調査したので Blog にまとめていきます。
実装方法
実装の仕方は、2 つに分けられます。
- Storyboard の ContainerView + Segue での実装
- ソースコードでの実装
それぞれ見ていきます。
Storyboard の ContainerView + Segue での実装
StackView の中に、ContainerView を追加
各 ContainerView と追加したい ViewController を Segue で連結
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 は完全に独立しています。
ソース全文は下記の通りです。
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 ステップを行わないといけません。
- Call
addChild()on your parent view controller, passing in your child.- Set the child’s frame to whatever you need, if you’re using frames.
- Add the child’s view to your main view, along with any Auto Layout constraints.
- Call didMove(toParent:) on the child, passing in your main view controller.
実際に利用するなら、UIViewController の Extension を作成する形になるかと思います。
追加する ChildView を条件により変更しない場合は、Storyboard を活用した ContainerView + Segue の方が断然楽でしょう。