読者です 読者をやめる 読者になる 読者になる

炊きたてのご飯が食べたい

定時に帰れるっていいね。自宅勤務できるっていいね。子どもと炊きたてのご飯が食べられる。アクトインディでは積極的にエンジニアを募集中です。

iOS のライフサイクルと Autolayout の付き合い方


Swift2.2, Xcode7

要素の frame サイズの取得や、タップジェスチャーや Observer の登録など、 iOS のどのライフサイクル時に設定するのが良いか悩むことが多かったので記事にまとめることにしました。Storyboard を利用して Autolayout を設定しているケースを想定して書いています。

レイアウトの設定

Autolayout で指定した要素の frame は、ViewDidLoad や ViewWillAppear で取得しようとしても、まだレイアウトが確定していない為、正しいサイズを取得することができません。 Autolayout を指定した要素のサイズの取得はレイアウトが確定する viewDidLayoutSubviews 又は layoutSubviews で行う。どちらも画面表示の再には複数回呼ばれるため、何度呼ばれても問題ない書き方で記述する必要があります。

UIViewController

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    // table の高さを Autolayout に反映する
    self.detailTableViewHeightConstraint.constant = self.detailTableVeiw.contentSize.height
}

UIView

override func layoutSubviews() {
    super.layoutSubviews()
    // table の高さを Autolayout に反映する
    self.detailTableViewHeightConstraint.constant = self.detailTableVeiw.contentSize.height
}

レイアウトに関係なく一度だけ登録すれば良い設定

タップジェスチャーや delegate など、レイアウトに関係なく、一度設定すれば良いものは ViewDidLoad 又は awakeFromNib に書きます。

UIViewController

override func viewDidLoad() {
    super.viewDidLoad()
    self.tableView.delegate = self
}

※ init に書いても良さそうです。

UIView

override func awakeFromNib() {
    super.awakeFromNib()
    self.initialize()
}

※ init に書いても良さそうです。

observerの設定

observer は登録したら 2重登録を防ぐ為、必ず解放してあげる必要があります。 ViewDidLoad で observer の登録を行うと ViewDidLoad の対となる箇所がない為、基本的には ViewWillAppear で行います。UIView は init の対として deinit を利用します。

UIViewController

viewWillAppear で登録 ViewWilldisapper で解放

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    NSNotificationCenter.defaultCenter().addObserver(
        self,
        selector: #selector(keyboardWillShow(_:)),
        name: UIKeyboardWillShowNotification,
        object: nil
    )
}

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
}

※ viewDidAppear, viewDidDisappear で登録と解放を行っても良さそうですが、特に理由もなく viewWillAppear で行ってます。

UIView

init で登録 deinit で解放。UIViewController で UIView を呼び出す設定にしていると思うので、きちんと画面遷移をしたら deinit が呼び出されることを確認しましょう。

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.textChanged(_:)), name: UITextViewTextDidChangeNotification, object: nil)
}

deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self)
}