参考
Switching Child View Controllers in iOS with Auto Layout
英語がわかるなら、上記のURLがすべて。
概要
下図の黄色のビューを、動的に入れ替える話。
コード
import UIKit
class ViewController: UIViewController {
enum CenterViewType {
case First
case Second
case Third
func id() -> String {
switch self {
case .First:
return "First"
case .Second:
return "Second"
case .Third:
return "Third"
}
}
}
@IBAction func firstButtonTapped(_ sender: Any) {
self.changeCenterView(.First)
}
@IBAction func secondButtonTapped(_ sender: Any) {
self.changeCenterView(.Second)
}
@IBAction func thirdButtonTapped(_ sender: Any) {
self.changeCenterView(.Third)
}
@IBOutlet weak var centerView: UIView!
var currentViewController: UIViewController?
func addSubview(_ subView:UIView, toView parentView:UIView) {
parentView.addSubview(subView)
var viewBindingsDict = [String: AnyObject]()
viewBindingsDict["subView"] = subView
parentView.addConstraints(
NSLayoutConstraint.constraints(
withVisualFormat: "H:|[subView]|",
options: [],
metrics: nil,
views: viewBindingsDict))
parentView.addConstraints(
NSLayoutConstraint.constraints(
withVisualFormat: "V:|[subView]|",
options: [],
metrics: nil,
views: viewBindingsDict))
}
func changeCenterView(_ type: CenterViewType) {
let identifier = type.id()
let newViewController = self.storyboard?.instantiateViewController(withIdentifier: identifier)
newViewController!.view.translatesAutoresizingMaskIntoConstraints = false
self.cycleFromViewController(self.currentViewController!, toViewController: newViewController!)
self.currentViewController = newViewController
}
func cycleFromViewController(_ oldViewController: UIViewController, toViewController newViewController: UIViewController) {
oldViewController.willMove(toParentViewController: nil)
self.addChildViewController(newViewController)
self.addSubview(newViewController.view, toView:self.centerView!)
// TODO: Set the starting state of your constraints here
newViewController.view.layoutIfNeeded()
// TODO: Set the ending state of your constraints here
UIView.animate(withDuration: 0.5,
animations: {
// only need to call layoutIfNeeded here
newViewController.view.layoutIfNeeded()
},
completion: { finished in
oldViewController.view.removeFromSuperview()
oldViewController.removeFromParentViewController()
newViewController.didMove(toParentViewController: self)
}
)
}
override func viewDidLoad() {
self.currentViewController = self.storyboard?.instantiateViewController(withIdentifier: CenterViewType.First.id())
let view = self.currentViewController!.view!
view.translatesAutoresizingMaskIntoConstraints = false
self.addChildViewController(self.currentViewController!)
self.addSubview(self.currentViewController!.view, toView: self.centerView)
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
コードの解説
"First","Second","Third"と表示されたボタンを押すと
@IBAction func firstButtonTapped(_ sender: Any) {
self.changeCenterView(.First)
}
@IBAction func secondButtonTapped(_ sender: Any) {
self.changeCenterView(.Second)
}
@IBAction func thirdButtonTapped(_ sender: Any) {
self.changeCenterView(.Third)
}
それぞれ、上記のアクションが呼び出され、どれを選んでも changeCenterView()
メソッドが呼び出される。
changeCenterView()
では、
func changeCenterView(_ type: CenterViewType) {
let identifier = type.id()
let newViewController = self.storyboard?.instantiateViewController(withIdentifier: identifier)
newViewController!.view.translatesAutoresizingMaskIntoConstraints = false
self.cycleFromViewController(self.currentViewController!, toViewController: newViewController!)
self.currentViewController = newViewController
}
storyboard id (identifier) から該当する子ViewControllerを生成し、 cycleFromViewController()
メソッドを呼び出す。
func cycleFromViewController(_ oldViewController: UIViewController, toViewController newViewController: UIViewController) {
oldViewController.willMove(toParentViewController: nil)
self.addChildViewController(newViewController)
self.addSubview(newViewController.view, toView:self.centerView!)
// TODO: Set the starting state of your constraints here
newViewController.view.layoutIfNeeded()
// TODO: Set the ending state of your constraints here
UIView.animate(withDuration: 0.5,
animations: {
// only need to call layoutIfNeeded here
newViewController.view.layoutIfNeeded()
},
completion: { finished in
oldViewController.view.removeFromSuperview()
oldViewController.removeFromParentViewController()
newViewController.didMove(toParentViewController: self)
}
)
}
cycleFromViewController()
では古いViewControllerを新しいViewControllerと取り替える。
重要なのが、 addSubview(toView:)
メソッド。
func addSubview(_ subView:UIView, toView parentView:UIView) {
parentView.addSubview(subView)
var viewBindingsDict = [String: AnyObject]()
viewBindingsDict["subView"] = subView
parentView.addConstraints(
NSLayoutConstraint.constraints(
withVisualFormat: "H:|[subView]|",
options: [],
metrics: nil,
views: viewBindingsDict))
parentView.addConstraints(
NSLayoutConstraint.constraints(
withVisualFormat: "V:|[subView]|",
options: [],
metrics: nil,
views: viewBindingsDict))
}
addSubview(toView:)
では、AutoLayoutを適切に設定してやることにより、子ViewControllerが元の黄色いView内にぴったり収まるようにしている(自力で子ViewControllerを入れ替えようとしてハマるのが、この部分)。
GitHubレポジトリ
出来上がったプロジェクトが以下。
https://github.com/JunSuzukiJapan/SwitchChildViewControllers