実行時に制約の優先度を変更するにはどうすればよいですか


87

動的な高さのビューがあり、実行時にこのビューの高さの優先度を変更しようとしています。

これが私のコードの一部です。

if (index == 0) {

    surveyViewHeightConstraint.constant = 0;
    surveyViewHeightConstraint.priority = 1000;

} else if (index == 1) {

    surveyViewHeightConstraint.constant = 163;
    surveyViewHeightConstraint.priority = 500;

}

ボタン操作でインデックスを変更しています。このコードを実行すると、次のエラーが発生します。

*** Assertion failure in -[NSLayoutConstraint setPriority:], /SourceCache/Foundation/Foundation-1141.1/Layout.subproj/NSLayoutConstraint.m:174

ここでの私の間違いは何ですか?

回答:


174

NSLayoutConstraintクラスリファレンスに記載されているように:

優先順位は、必須から必須へ、または必須から非必須へと変更することはできません。NSLayoutPriorityRequiredOS XまたはUILayoutPriorityRequirediOSのの優先度が低い優先度に変更された場合、または制約がビューに追加された後に低い優先度が必要な優先度に変更された場合、例外がスローされます。ビューに制約がインストールされた後でも、あるオプションの優先度から別のオプションの優先度への変更は許可されます。

1000ではなく999の優先度を使用します。技術的には絶対に必要というわけではありませんが、他の何よりも優先度が高くなります。


4
良い答えをありがとうございますが、1000ではなく999を使用すると、高さの制約に0を割り当ててもビューを非表示にできません。
Le'Kirdok 2015

それは別の問題です。他の矛盾する制約があるかもしれません。コードがクラッシュしなくなってもビューアニメーションが正しくない場合は、この質問を解決済みとしてマークしてください。次に別のものを開きます。
Cyrille 2015

いいですね、異なる値(
999、900、500

7
真剣に?iOS開発で正常に機能しているものはありますか?非常に多くのことを手動で行う必要があり、および/またはシステムのバグやサポートされていない原因となる大量の回避コードを実装する必要がありました。建設的なコメントではなく申し訳ありません。
ルテイン2018年

7
この制限はiOS13で削除されたようです。制約をからrequiredに変更してみましたがdefaultLow、機能しました。iOS 12でクラッシュするために使用されたのと同じコード
StevenVandeweghe19年

53

Cyrilleの答えに少し追加したいと思います。

コードで制約を作成する場合は、アクティブにする前に必ず優先度を設定してください。例えば:

surveyViewHeightConstraint = [NSLayoutConstraint constraintWithItem:self
                                               attribute:NSLayoutAttributeHeight
                                               relatedBy:NSLayoutRelationEqual
                                                  toItem:self.superview
                                               attribute:NSLayoutAttributeHeight
                                              multiplier:1
                                                constant:0];
surveyViewHeightConstraint.active = YES;
surveyViewHeightConstraint.priority = 999;

これにより、実行時例外が発生します。

優先度を必須からインストールされていない制約に変更する(またはその逆)ことはサポートされていません。

正しい順序は次のとおりです。

surveyViewHeightConstraint.priority = 999;
surveyViewHeightConstraint.active = YES;

Swiftバージョン4以降の場合

constraintName.priority = UILayoutPriority(rawValue: 999)


8

これを常に処理する方法は、制約定数を変更せず、優先度のみを変更することです。たとえば、状況によっては2つの高さの制約があります。

 heightConstraintOne (who's height is set in the storyboard at 150, and priority set at 750)
 heightConstraintTwo (who's height is set in the storyboard at 0, and priority set at 250)

ビューを非表示にする場合は、次のようにします。

heightConstraintTwo.priority = 999;

同様に、ビューを表示したい場合:

heightConstraintTwo.priority = 250;

5
クール、それを得た:使用999の代わりに1000のおかげで
brainray

3

このシナリオが誰かに役立つことを願っています:私には2つの制約がありました。それらは互いに反対でした。つまり、同時に満たすことができなかったということです。インターフェイスビルダーでは、一方の優先度が1000で、もう一方の優先度が1000未満でした。コードでそれらを変更すると、次のエラーが発生しました。

キャッチされなかった例外 'NSInternalInconsistencyException'が原因でアプリを終了します、理由: 'インストールされた制約に優先度を必須から非に変更する(またはその逆)ことはサポートされていません。優先度250を通過し、既存の優先度は1000でした。

次に、viewDidLoad関数で.defaultHigh値と.defaultLow値を使用してそれらを初期化したところ、問題が修正されました。


1

NSLayoutConstraints class内部に よるとUIKit Module

制約の優先度レベルがUILayoutPriorityRequired未満の場合、それはオプションです。優先度の高い制約は、優先度の低い制約の前に満たされます。制約の満足度は、すべてまたはまったくではありません。制約 'a == b'がオプションの場合、それは 'abs(ab)'を最小化しようとすることを意味します。このプロパティは、初期設定の一部として、またはオプションの場合にのみ変更できます。ビューに制約が追加された後、優先度がNSLayoutPriorityRequiredとの間で変更されると、例外がスローされます。

例:-UIButtonさまざまな優先順位を持つ制約-

 func setConstraints() {
        buttonMessage.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint(item: buttonMessage, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1.0, constant: -10).isActive = true

        let leading = NSLayoutConstraint(item: buttonMessage, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 10)

        leading.isActive = true


        let widthConstraint = NSLayoutConstraint(item: buttonMessage, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100)

        let heightConstraint = NSLayoutConstraint(item: buttonMessage, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 50)


        let trailingToSuperView = NSLayoutConstraint(item: buttonMessage, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 0)

        trailingToSuperView.priority = 999
        trailingToSuperView.isActive = true

        //leading.isActive = false//uncomment & check it will align to right of the View

        buttonMessage.addConstraints([widthConstraint,heightConstraint])

         }  

1
いいえ、で制約を作成しないでくださいviewDidLayoutSubviews。そのメソッドは数回呼び出すことができ、そこで重複する制約を作成するとアプリがクラッシュする可能性があります。
mph

0

私は同じ問題に直面していました。iOS 13で前述した回答の一部は正常に機能しますが、iOS12ではクラッシュにつながります。

ストーリーボードの特定の制約に対してIBOutletNSLayoutConstraintを作成し、優先度を1000に維持することで、この問題を修正できました。以下は修正のコードです。

if (Condition) {
    surveyViewHeightConstraint.constant = 0;
    surveyViewHeightConstraint.isActive = false;
} else if (index == 1) {
    surveyViewHeightConstraint.constant = 163;
    surveyViewHeightConstraint.isActive = True;
}

お役に立てれば!!!乾杯


-2

これで、制約にIBOutlet接続を使用して、このコードを使用できます。

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