いつレイアウト制約をアクティブ化/非アクティブ化できますか?


104

IBで複数の制約セットを設定しましたが、状態によってプログラムでそれらを切り替えたいのですが。ありますconstraintsAIBからインストールとしてマークされているすべてのそれらの出口コレクションは、およびconstraintsBすべての出口コレクションは、IBにアンインストールされます。

次のように、プログラムで2つのセットを切り替えることができます。

NSLayoutConstraint.deactivateConstraints(constraintsA)
NSLayoutConstraint.activateConstraints(constraintsB)

しかし... いつそれをするべきかわからない。で一度はできるはずですが、うまくviewDidLoadいきません。私は呼び出ししようとしたview.updateConstraints()view.layoutSubviews()制約を設定した後、しかし無駄に。

viewDidLayoutSubviewsすべてに制約を設定すると、期待どおりに機能することがわかりました。2つのことを知りたいと思います...

  1. なぜこの動作になるのですか?
  2. viewDidLoadから制約をアクティブ化/非アクティブ化することは可能ですか?

2
deactivateConstraintsとactivateConstraintsがviewWillLayoutSubviewsで機能したということですか?私はそれを試してみましたが、そこでも、viewDidLoadでも機能しませんでした。それは一種のviewDidAppearで機能しました。新しい制約を配置する場所にビューが表示されましたが、回転して横向きにすると、ビューはIBで設定された制約によって決定された位置に戻りました(縦向きに回転したときもビューはそのままでした)。制約をログに記録すると、正しいものが表示されました(新しくアクティブになったもの)。これは私にはバグのようです。
rdelmar 2014

1
はい、それらは有効であり(viewDidAppearで機能しました)、viewWillLayoutSubviewsのデフォルト実装がないため、superを呼び出す必要はありません(とにかくsuperを呼び出して試しましたが、違いはありませんでした)。
rdelmar 2014

1
@rdelmarはさらにテストする機会を得た...私が実際にあなたが説明したのと同じ動作を得たことを確認できます...最初はviewDidAppearで動作しますが、回転すると元に戻ります。
tybro0103 14

3
どうやら、この目的のために、IBにインストールされていない制約をマークすることはできません。その情報をここに見つけました:stackoverflow.com/questions/27663249/…そして、それは私のために問題を解決しました。
ステファン

1
質問で説明したのと同じ方法で制約を実装しましたが、viewDidAppearでそれらの一部をアクティブ化/非アクティブ化しました。これは機能しましたが、要素がすぐに位置を変えることがわかりました(マイナーですが望ましくない問題)。viewWillAppearまたはviewDidLoadで変更を加えても機能しませんでした。しかし、この質問を読んだ後、viewDidLayoutSubviewsでその変更を試みました。これは機能し、位置の変更はユーザーに表示されなくなります。(viewWillLayoutSubviewsでも機能しました)。そのヒントをありがとう!
ピースタイプ

回答:


185

でアクティブ化および非アクティブ化NSLayoutConstraintsしますviewDidLoad、と私はそれで何の問題もありません。だからそれは動作します。アプリと私の設定には違いがあるはずです:-)

私は私のセットアップについて説明します-多分それはあなたにリードを与えることができます:

  1. 私は設定しました @IBOutletsアクティブ化/非アクティブ化する必要があるすべての制約ます。
  2. の中に ViewController弱くないクラスプロパティに制約を保存しています。これは、制約を非アクティブにした後、再度アクティブにできないことがわかったためです-それはnilでした。そのため、無効にすると削除されるようです。
  3. 私はNSLayoutConstraint.deactivate/activateあなたのように使用しません、私はconstraint.active = YES/ を使用しますNO代わりに。
  4. 制約を設定した後、を呼び出しますview.layoutIfNeeded()

131
「弱いものではないクラスプロパティに制約を保存する」おかげで多くの時間を節約できました。
OpenUserX03

10
「弱くないクラスプロパティに制約を保存する」:これにより、多くの心痛を軽減できました。nilオブジェクトでセレクターを呼び出していることを知りませんでした。ありがとう!!
static0886

4
「非アクティブ」な制約は自動レイアウトでは無視されず、削除されることに注意してください。制約をアクティブ化/非アクティブ化すると、実際に制約が追加および削除されます。以前に設定.active = falseした制約を追加した後、それらをアクティブに設定するまで無視されることを想定して、競合する自動レイアウトのデバッグにしばらく費やしました。
lbarbosa 2016年

1
弱いものではないクラスプロパティに制約を保存します。これにより時間を大幅に節約できます。これがないと、いくつかの結果が混在していました。ありがとう!
MegaManX 2016

3
Appleのドキュメントによると、制約をアクティブ化または非アクティブ化する、この制約によって管理されるアイテムの最も近い共通の祖先であるビューでaddConstraint(:)およびremoveConstraint( :) が呼び出されます。addConstraint(:)またはremoveConstraint( :)を直接呼び出す代わりに、このプロパティを使用します。したがって、IBOutletが強い場合を除いて、制約が非アクティブ化されると削除され、制約への強い参照が残っていないように見えます。したがって、制約は削除されます。私見これはほとんどバグか、少なくとも非常に予期しない動作です。
Olle Raab

52

たぶん、あなたは可能性があなたをチェック@properties置き換える、weakstrong

時々、それはあなたが再び使用することができないように、active = NOsetのself.yourConstraint = nilためですself.yourConstraint


5
Swift言語ガイドに記載されているように、プロパティはデフォルトで強力であるためweak、削除することもできます。
ジョナサンカブレラ

30
override func viewDidLayoutSubviews() {
// do it here, after constraints have been materialized
}

1
私のビューコントローラーは子ビューコントローラーなので、 "didLayoutSubviews"でそれを行うのが唯一の方法のようです!! ご参考までに。
TalL 2017年

これが唯一の有効な答えです
Yunus ErenGüzel'29年

@TalL子ビューコントローラ自体の制約ですか、それともサブビューですか?
ステファン

これは最高です
ACAkgul

14

あなたが経験している問題は、AFTER viewDidLoad()が呼び出されるまで制約がビューに追加されないことが原因であると思います。いくつかのオプションがあります。

A)レイアウト制約をIBOutletに接続し、これらの参照によってコード内の制約にアクセスできます。viewDidLoad()キックオフの前にコンセントが接続されているため、制約にアクセスでき、そこで制約をアクティブ化および非アクティブ化し続けることができます。

B) UIViewのconstraints()機能を使用してさまざまな制約にアクセスする場合はviewDidLayoutSubviews()、キックオフしてそこで実行するのを待つ必要があります。これは、nibからビューコントローラーを作成した後の最初のポイントであるため、インストールされている制約があります。layoutIfNeeded()終わったら電話をかけることを忘れないでください。これには、適用する変更がある場合にレイアウトパスが2回実行されるという欠点があり、無限ループがトリガーされる可能性がないことを確認する必要があります。

警告:無効化された制約は メソッドから返されませんconstraints() !つまり、後で再びオンにするつもりで制約を無効にした場合は、その参照を保持する必要があります。

C)ストーリーボードアプローチを忘れて、代わりに手動で制約を追加できます。あなたがこれを行っているのでviewDidLoad()、その場でレイアウトを変更するのではなく、オブジェクトの存続期間全体で1回だけ行うことが意図されているので、これは許容できる方法であるはずです。


10

priorityプロパティを調整して「有効」および「無効」にすることもできます(たとえば、有効にする場合は750、無効にする場合は250)。何らかの理由でactiveBOOLを変更しても、UIには何の影響もありませんでした。不要でlayoutIfNeeded、viewDidLoadで、またはその後いつでも設定および変更できます。


非常に良い提案です。制約の優先度の変更はviewWillTransition(to:, with:)またはviewWillLayoutSubviews()で機能し、代替のすべての制約をストーリーボードに「インストール済み」として保持できます。制約の優先度は非必須から必須に変更されない場合があるため、以下の値を使用します1000。一方、制約のアクティブ化(追加)および非アクティブ化(削除)はviewDidLayoutSubviews()、でのみ機能し、-s strong @IBOutletへの参照を維持する必要がありNSLayoutConstraintます。
Gary

「何らかの理由でアクティブなBOOLを変更しても、UIに影響はありませんでした」。ここに基づいて。私は考えてあなたが実行時に1000年の優先度で制約を変更することはできません。あなたはそれを非アクティブにしたい場合は、.... 999に初期の優先順位を設定するか、下げる必要があります
ハニー

問題のデバッグが困難になる可能性があり、質問に回答しないため、この文には同意しません。優先度を250に設定しても、制約は「非アクティブ化」されません。それでも効果があり、レイアウトに影響を与えます。ほとんどの場合、それは制約を「非アクティブ化」するように見えるかもしれませんが、すべての場合に間違いなくそうです。(特に、私がこの質問への答えを見つけるように導いたケースではありません)
トゥマタ

これは、「インストール済みの制約での優先から必須への優先順位の変更(またはその逆)はサポートされていません。優先順位は250で、既存の優先順位は1000でした。」のようなクラッシュを引き起こす可能性があります。
Karthick Ramesh

8

未使用の制約を非アクティブ化する適切なタイミング:

-(void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];

    self.myLittleConstraint.active = NO;
}

覚えておいてviewWillLayoutSubviews大丈夫、ここには重い計算して、複数回呼び出されるだろうか?

注:一部の制約を後でアクティブにしたい場合は、常にstrongそれらへの参照を保存してください。


2
私にとって唯一の信頼できる方法は、で制約を調整することviewDidLayoutSubviews()です。viewWillLayoutSubviews()私の場合、制約の調整が機能しません。
petrsyn 2016年

6

ビューが作成されると、次のライフサイクルメソッドが順番に呼び出されます。

  1. loadView
  2. viewDidLoad
  3. viewWillAppear
  4. viewWillLayoutSubviews
  5. viewDidLayoutSubviews
  6. viewDidAppear

さてあなたの質問に。

  1. なぜこの動作になるのですか?

回答:ビューのビューに制約を設定しようとすると、viewDidLoadその境界がないため、制約を設定できません。viewDidLayoutSubviewsビューの境界が確定された後のみです。

  1. viewDidLoadから制約をアクティブ化/非アクティブ化することは可能ですか?

回答:いいえ。理由は上記で説明しました。


viewControllerライフサイクルの説明では、ビューが最初にロードされ、次にviewDidLoadが呼び出される方法について説明しました。しかし、viewDidLoadが呼び出された時点ではビューが作成されないとも言いましたが、これは明らかに矛盾しています。また、自分でテストして、ビューにサブビューを追加できるため、viewDidLoadが呼び出されたときにビューが作成されていることを確認できます。
ABakerSmith、2015

ビューが作成されて読み込まれるので、viewDidLoadは問題ありません。実際には、制約をアクティブ化する場所は主にパフォーマンスに影響します。元の問題は制約がアクティブ化された場所とは無関係であったと思います。 stackoverflow.com/questions/19387998/…–
ゲイブ

@ABakerSmith私は私の答えをより明確にするために編集しました。
Sumeet、2015年

1

- (void)updateConstraints(目的c)のオーバーライドで通常どおりに制約を設定している限り、strongアクティブと非アクティブの制約を使用した初期値のリファレンスが見つかりました。そして、ビューサイクルの他の場所で、必要なものを非アクティブ化またはアクティブ化してから、を呼び出しlayoutIfNeededても問題はありません。

最初の初期化とレイアウトの後でs updateConstraintsを呼び出す限り、主なことは、制約のオーバーライドを常に再利用し、制約のアクティブ化を分離することではありませんupdateConstraint。その後、ビューサイクルのどこに問題があるように見えます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.