xib上でhiddenに設定したconstraintsのactivateが正しく機能しない問題への対応
iOSアプリ開発において, xib上でPortrait用・Landscape用のconstraintsをそれぞれ設定し, コードでactivate/deactivateを切り替えることが往々にしてあると思います.
activate/deactivateの処理が正しく機能しなくなる現象に遭遇したので, その対応方法についてのメモです.
isActivateを操作しても巻き戻るケース
条件は明確です.
- XCode11.5 (XCode11.x 全ての可能性が高いです. 10.xで起きていたかは不明です)
- xib上でHiddenに設定した
NSLayoutConstraint
であること - その
NSLayoutConstraint
をviewWillLayoutSubviews
からviewDidLayoutSubviews
の間に操作すること
これらの条件を満たすことで, isActivate
に代入した値が反映されず, 巻き戻る現象が発生します.
これは, 例えば以下のようなコードの場合が該当します.
対策方法
1. 回転時の対応
回転の場合のみであれば, viewWillTransition(to:with:)
を使うという方法があります.
余談ですが, viewWillLayoutSubviews
でsetNeedsUpdateConstraints
を呼び出すのはフィードバックループ (無限ループ) が発生する可能性があるので, 公式のガイドではやってはいけない呼び出しとされています. 1
その為, ここではその方法を取りません.
2. 外観モードが変更された場合
これだけだと外観モードの変更時に壊れるという不具合があります.
外観モードとはiOS13から導入されたOSにあるライトモード・ダークモードの設定のことです.
外観モードが変更され, 諸々の処理が終わった後でないと適用できない為, アドホックな対応が必要となります.
ここで大切なのは, completion
で実行するという点です. alongsideTransition
側で実行しても機能しない点に注意が必要です. 謎ですが.
3. ViewController表示時
これでもまだ不足しています.
iOS11.xなどにおいて (iOS13未満だと思われますが, 対象のOSについては特に調査していません) ViewController表示時のconstraintsが壊れるという現象があります.
これについてはviewDidAppear(_:)
を用いることで解消することができます.
根本的に回避する方法
恐らくこれら全ての対応を行うことで解消されると思われるものの, 将来的なバージョンにおいて挙動がまた変わる可能性があるので根本的に対応するのであれば, xib上でhiddenにしないという方法を推奨します.
とはいえ, このようなconstraintsになるのは得てして複雑な場合なので難しいのですが.
なお、記事中のサンプルコードはGitHub上に公開しています.
記事が気に入ったらチップを送ることができます!
You can give me a cup of coffee :)
Kyash ID: soranoba
Amazon: Wish List
GitHub Sponsor: github.com/sponsors/soranoba
PayPal.Me: paypal.me/soranoba
(Updated: )