Xibファイルでレイアウトを定義したUIViewControllerで*LayoutGuideを使う

XIBファイルで開発を進めているけど UIViewController のサブビューが UINavigationBar にめり込んでつらいという話をされたので対策を考えた。

XIBファイルベースで開発するUIViewControllerサブクラスは FromNibViewController のサブクラスとして実装することを前提として。

class FromNibViewController: UIViewController {
    
    // MARK: -- Statics
    static var kTopLayoutConstraint: String { return "TopLayoutConstraint" }
    static var kBottomLayoutConstraint: String { return "BottomLayoutConstraint" }
    
    // MARK: -- Properties
    var topLayoutConstraint: NSLayoutConstraint!
    var bottomLayoutConstraint: NSLayoutConstraint!
    
    // MARK: -- View lifecycles
    func scanLayoutConstraint() {
        for constraint: NSLayoutConstraint in self.view.constraints {
            if (constraint.identifier == FromNibViewController.kTopLayoutConstraint) {
                self.topLayoutConstraint = constraint
            }
            else if constraint.identifier == FromNibViewController.kBottomLayoutConstraint {
                self.bottomLayoutConstraint = constraint
            }
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.scanLayoutConstraint()
    }
    
    override func viewWillLayoutSubviews() {
        // do something
        self.topLayoutConstraint.constant = self.topLayoutGuide.length
        self.bottomLayoutConstraint.constant = self.bottomLayoutGuide.length
        
        super.viewWillLayoutSubviews()
    }
    
}

あとはXIBファイル側では、基本的には UIViewController を作成したときの Also create XIB file のままAutoLayoutを指定していく。
最後に TopLayoutGuideBottomLayoutGuide を当てたい NSLayoutConstraintIdentifierTopLayoutConstraintBottomLayoutConstraint を指定してあげる。

元のレイアウトはこんな感じで上下左右余白0

TopのNSLayoutConstraintにIdentifierを設定する

BottomのNSLayoutConstraintにIdentifierを設定する

こうすれば変にイニシャライザをいじったり func loadView() を書く必要もないです。

self.view がピンクで、 subview が青い画面のとき。

余談ですが、この2行を viewWillLayoutSubviews() 以外の、 updateViewConstraints() などで実行するとレイアウトが最適化されていない状態で画面遷移が行われたりするので気をつけて下さい。

self.topLayoutConstraint.constant = self.topLayoutGuide.length
self.bottomLayoutConstraint.constant = self.bottomLayoutGuide.length

ソース