ViewではなくViewControllerで画面部品を細かく分けて画面を組み立てるサンプルです。
下にToolbarがあり、それ以外はWebViewという簡単な画面です。
簡単な内容ですが、2つ罠に嵌ってしまったため、誰かの助けになることを期待して情報を残しておきます。

WebViewController
画面表示以外のコードがありません。
外からURLを指定して読み込む機能などは、必要な場合各自追加をお願いします🙇♂️
import UIKit
import WebKit
class WebViewController: UIViewController {
var webView: WKWebView!
override func loadView() {
let configuration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: configuration)
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://google.com")!
let request = URLRequest(url: url)
webView.load(request)
}
}
ToolbarViewController
こちらも画面表示以外のコードがありません。
import UIKit
class ToolbarViewController: UIViewController {
var toolbar: UIToolbar!
override func loadView() {
toolbar = UIToolbar(frame: .zero)
let back = UIBarButtonItem(title: "back", style: .plain, target: nil, action: nil)
toolbar.items = [back]
view = toolbar
}
}
画面を組み立てる
import UIKit
class ViewController: UIViewController {
let toolbar = ToolbarViewController()
let webView = WebViewController()
override func loadView() {
view = UIView(frame: .zero)
webView.view.translatesAutoresizingMaskIntoConstraints = false
addChild(webView)
view.addSubview(webView.view)
webView.didMove(toParent: self)
toolbar.view.translatesAutoresizingMaskIntoConstraints = false
addChild(toolbar)
view.addSubview(toolbar.view)
toolbar.didMove(toParent: self)
let guide = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
webView.view.topAnchor.constraint(equalTo: view.topAnchor),
webView.view.leftAnchor.constraint(equalTo: view.leftAnchor),
webView.view.bottomAnchor.constraint(equalTo: toolbar.view.topAnchor),
webView.view.rightAnchor.constraint(equalTo: view.rightAnchor),
toolbar.view.leftAnchor.constraint(equalTo: view.leftAnchor),
toolbar.view.bottomAnchor.constraint(equalTo: guide.bottomAnchor),
toolbar.view.rightAnchor.constraint(equalTo: view.rightAnchor)
])
}
}
あまり難しい箇所もなくできました!
小さなViewControllerは、Viewが1個ならAutoLayoutのコードを書く必要もないです。
2,3個に押さえて入れ子にすれば、AutoLayoutのコードは短くわかりやすい範囲に収まります。
そのためxibを使わなくても書きやすくなる場合が多いです。
(逆にVCの数だけxibが増殖すると、管理がちょっと面倒かも)
注意点
viewに直接UIToolbarを設定すること
var UIViewController.view
に直接UIToolbarを設定してください。
以下のようにUIViewの中にUIToolbarをaddSubview
してはいけません。
override func loadView() {
view = UIView(frame: .zero)
toolbar = UIToolbar(frame: .zero)
toolbar.translatesAutoresizingMaskIntoConstraints = false
let back = UIBarButtonItem(title: "back", style: .plain, target: nil, action: nil)
toolbar.items = [back]
view.addSubview(toolbar)
NSLayoutConstraint.activate([
toolbar.topAnchor.constraint(equalTo: view.topAnchor),
toolbar.leftAnchor.constraint(equalTo: view.leftAnchor),
toolbar.bottomAnchor.constraint(equalTo: view.bottomAnchor),
toolbar.rightAnchor.constraint(equalTo: view.rightAnchor)
])
}
UIViewの中のUIToolbarを入れる構成では ToolbarのSafe Area対応が機能しません!
以下のような表示になってしまいます。

UIViewControllerの中で self.view.translatesAutoresizingMaskIntoConstraints = false
しない
上記サンプルの例であれば、WebViewControllerのloadView()
で
self.view.translatesAutoresizingMaskIntoConstraints = false
しても期待通りに動作します。
しかしそれをした場合 WebViewControllerをモーダル表示すると正しく表示されません!
おそらくモーダル表示では、Auto Resizing Maskの自動変換が動作する前提となっているのでしょう。
Toolbarなら問題ないですが(さすがにモーダル表示しないでしょう)
統一しておけば迷うこともないので、UIViewControllerを生成した側が外から
vc.view.translatesAutoresizingMaskIntoConstraints = false
をしましょう。