web-dev-qa-db-ja.com

UIStackView「隠された」隠されたビューの「制約を同時に満たすことができません」

UIStackViewの「行」がつぶされると、AutoLayout警告がスローされます。ただし、これらは正常に表示され、これらの種類のログ以外に問題はありません。

同時に制約を満たすことができません。おそらく、次のリストにある制約の少なくとも1つは、望ましくないものです。これを試してください:(1)各制約を見て、予期しないものを見つけてください。 (2)不要な制約を追加したコードを見つけて修正します。 (注:理解できないNSAutoresizingMaskLayoutConstraintsが表示されている場合は、UIViewプロパティのドキュメントを参照してくださいtranslatesAutoresizingMaskIntoConstraints)(

そのため、これをどのように修正するかはまだわかりませんが、単にうっとうしいこと以外に何も壊さないようです。

誰もそれを解決する方法を知っていますか?興味深いことに、レイアウト制約には'UISV-hiding'というタグが頻繁に付けられます。これは、サブインスタンスまたはこのインスタンスの何かの最小高さを無視する必要があることを示していますか?

89
Ben Guild

この問題は、サブビューをUIStackView内から非表示に設定すると、アニメーション化するために最初に高さをゼロに制限するために発生します。

次のエラーが表示されました。

2015-10-01 11:45:13.732 <redacted>[64455:6368084] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x7f7f5be18c80 V:[UISegmentedControl:0x7f7f5bec4180]-(8)-|   (Names: '|':UIView:0x7f7f5be69d30 )>",
    "<NSLayoutConstraint:0x7f7f5be508d0 V:|-(8)-[UISegmentedControl:0x7f7f5bec4180]   (Names: '|':UIView:0x7f7f5be69d30 )>",
    "<NSLayoutConstraint:0x7f7f5bdfbda0 'UISV-hiding' V:[UIView:0x7f7f5be69d30(0)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7f7f5be18c80 V:[UISegmentedControl:0x7f7f5bec4180]-(8)-|   (Names: '|':UIView:0x7f7f5be69d30 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

私がやろうとしていたことは、UIView内にUIStackViewを配置することで、各エッジにUISegmentedControlのインセットが8ポイント挿入されていました。

非表示に設定すると、コンテナビューの高さをゼロに制限しようとしますが、上から下に一連の制約があるため、競合が発生しました。

この問題を解決するために、必要に応じてUISV-hiding制約が優先されるように、8ptの上位および下位の制約の優先度を1000から999に変更しました。

196
liamnichols

私は解決が容易ではない同様の問題を抱えていました。私の場合、スタックビューにスタックビューが埋め込まれていました。内部UIStackViewには、2つのラベルと0以外の間隔が指定されていました。

AddArrangedSubview()を呼び出すと、次のような制約が自動的に作成されます。

V:|[innerStackView]|              | = outerStackView

  V:|[label1]-(2)-[label2]|       | = innerStackView

InnerStackViewを非表示にしようとすると、あいまいな制約の警告が表示されます。

理由を理解するために、最初に、innerStackView.spacing0と等しいときに、このが発生しない理由を見てみましょう。 innerStackView.hidden = trueを呼び出すと、@ liamnicholsは正しかった... outerStackViewはこの呼び出しを魔法のようにインターセプトし、0 heightUISV-hiding優先度1000で制約します(必須)。おそらく、これは、UIView.animationWithDuration()ブロック内で非表示のコードが呼び出された場合に、スタックビューの要素をビュー外でアニメーション化できるようにするためです。残念ながら、この制約が追加されるのを防ぐ方法はないようです。それでも、次のことが発生するため、「制約を同時に満たすことができません」(USSC)警告は表示されません。

  1. label1の高さは0に設定されます
  2. 2つのラベルの間隔はすでに0として定義されています
  3. label2の高さは0に設定されます
  4. innerStackViewの高さは0に設定されます

これらの4つの制約が満たされることは明らかです。スタックビューは、すべてを0の高さのピクセルに単純化します。

バグのある例に戻り、spacing2に設定すると、次の制約があります。

  1. label1の高さは0に設定されます
  2. 2つのラベルの間隔は、スタックビューによって、優先度1000で2ピクセルとして自動的に作成されました。
  3. label2の高さは0に設定されます
  4. innerStackViewの高さは0に設定されます

スタックビューの高さを両方とも0ピクセルにすることはできず、その内容を2ピクセルにすることもできません。制約を満たせません。

注:より簡単な例でこの動作を確認できます。 UIViewをスタックビューに配置されたサブビューとして追加するだけです。次に、そのUIViewに高さ制限を1000の優先度で設定します。それで、hideを呼び出してみてください。

注:何らかの理由で、これはスタックビューがUICollectionViewCellまたはUITableViewCellのサブビューである場合にのみ発生しました。ただし、内部スタックビューを非表示にした後、次の実行ループでinnerStackView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)を呼び出すことにより、セル外でこの動作を再現できます。

注:UIView.performWithoutAnimationsでコードを実行しようとしても、スタックビューには高さ0の制約が追加され、USSC警告が発生します。


この問題には少なくとも3つの解決策があります。

  1. スタックビュー内の要素を非表示にする前に、それがスタックビューかどうかを確認し、そうであれば、spacingを0に変更します。これは迷惑なのは、コンテンツを再度表示するたびにプロセスを元に戻す(および元の間隔を記憶する)必要があるためです。
  2. スタックビューで要素を非表示にする代わりに、removeFromSuperviewを呼び出します。プロセスを逆にすると、覚えておく必要があるため、これはさらに面倒ですwhere削除されたアイテムを挿入します。 removeArrangedSubviewを呼び出して非表示にするだけで最適化できますが、実行する必要のある簿記がたくさんあります。
  3. UIViewでネストされたスタックビュー(ゼロ以外のspacingを持つ)をラップします。少なくとも1つの制約を不要な優先度(999以下)として指定します。これは、簿記を行う必要がないため、最適なソリューションです。この例では、スタックビューとラッパービューの間に1000のトップ、リーディング、トレーリングの制約を作成し、スタックビューの下部からラッパービューに999の制約を作成しました。この方法では、外側のスタックビューが高さゼロの制約を作成するときに、999制約が壊れており、USSC警告は表示されません。 (注:これは、 ICollectionViewCellサブクラスのcontentView.translatesAutoResizingMaskToConstraintsをfalse)に設定する必要がある場合の解決策に似ています

要約すると、この動作が発生する理由は次のとおりです。

  1. スタックビューにマネージサブビューを追加すると、Appleは自動的に1000の優先度制約を作成します。
  2. スタックビューのサブビューを非表示にすると、Appleは自動的に高さ0の制約を作成します。

Appleが(1)制約の優先度(特にスペーサー)を指定できるか、(2)自動UISV- hiding制約により、この問題は簡単に解決されます。

48
Senseful

ほとんどの場合、このエラーは、競合を排除するために制約の優先順位を下げることで解決できます。

6
Luciano Almeida

ビューを非表示に設定すると、UIStackviewはそれをアニメーション化しようとします。その効果が必要な場合は、制約が競合しないように適切な優先度を設定する必要があります(多くの人が上記で提案したように)。

ただし、アニメーションを気にしない(おそらくViewDidLoadで非表示にしている)場合は、removeFromSuperviewを単純化できます。見る。

2
Oren

埋め込みスタックビューでも同じエラーが発生しましたが、実行時にはすべて正常に機能していました。

親スタックビューを非表示にする前に、最初にすべてのサブスタックビューを非表示にする(isHidden = trueを設定する)ことで制約エラーを解決しました。

これを行うと、サブアレンジされたビューを削除して、それらを再度追加する必要がある場合に備えてインデックスを維持する複雑さがなくなりました。

お役に立てれば。

1
Will Stevens

まず、他の人が示唆しているように、制御できる制約、つまりUIStackViewに固有の制約が優先度999に設定されていないことを確認して、ビューが非表示になったときにオーバーライドできるようにします。

それでも問題が発生する場合は、非表示のStackViewの間隔が原因である可能性があります。 私の解決策は、スペーサーとしてUIViewを追加し、UIStackViewの間隔をゼロに設定することでした。次に、View.heightまたはView.width制約(垂直または水平スタックに応じて)をStackViewの間隔に設定します。

次に、新しく追加されたビューのコンテンツハグとコンテンツ圧縮抵抗の優先順位を調整します。親StackViewの分布も変更する必要がある場合があります。

上記のすべては、Interface Builderで実行できます。さらに、新しく追加されたビューの一部をプログラムで非表示/非表示解除して、不要な間隔を空ける必要がある場合があります。

1
Peter Coyle

最近、UIStackViewを非表示にするときに、自動レイアウトエラーに取り組みました。 UIViewsで大量のブックキーピングとラッピングスタックを行うのではなく、parentStackView用のアウトレットと、非表示/再表示したい子供用のアウトレットを作成することにしました。

@IBOutlet weak var parentStackView: UIStackView!
@IBOutlet var stackViewNumber1: UIStackView!
@IBOutlet var stackViewNumber2: UIStackView!

ストーリーボードでは、私のparentStackは次のようになります。

enter image description here

これには4人の子があり、それぞれの子の内部には多数のスタックビューがあります。スタックビューを非表示にするときに、スタックビューでもあるUI要素がある場合、自動レイアウトエラーのストリームが表示されます。非表示にするのではなく、削除することを選択しました。

私の例では、parentStackViewsには4つの要素の配列が含まれています:上部スタックビュー、StackViewNumber1、スタックビュー番号2、および停止ボタン。 arrangedSubviewsのインデックスは、それぞれ0、1、2、3です。隠したいときは、parentStackView'sarrangedSubviews配列から単純に削除します。弱くないので、メモリ内に残り、後で希望するインデックスに戻すことができます。私はそれを再初期化していないので、必要になるまでハングアウトしますが、メモリを膨張させません。

基本的に、次のことができます...

1)親スタックおよび非表示/再表示する子のIBOutletsをストーリーボードにドラッグします。

2)非表示にする場合は、parentStackView'sarrangedSubviews配列から非表示にするスタックを削除します。

3)UIView.animateWithDurationself.view.layoutIfNeeded()を呼び出します。

最後の2つのstackViewはweakではないことに注意してください。それらを再表示するときのために、それらを保持する必要があります。

StackViewNumber2を非表示にしたいとしましょう:

parentStackView.removeArrangedSubview(stackViewNumber2)
stackViewNumber2.removeFromSuperview()

次に、アニメーション化します。

UIView.animate(withDuration: 0.25,
               delay: 0,
               usingSpringWithDamping: 2.0,
               initialSpringVelocity: 10.0,
               options: [.curveEaseOut],
               animations: {
                self.view.layoutIfNeeded()
},
               completion: nil)

後でstackViewNumber2を「再表示」したい場合は、必要なparentStackViewarrangedSubViewsインデックスに挿入して、更新をアニメートするだけです。

parentStackView.removeArrangedSubview(stackViewNumber1)
stackViewNumber1.removeFromSuperview()
parentStackView.insertArrangedSubview(stackViewNumber2, at: 1)

// Then animate it
UIView.animate(withDuration: 0.25,
               delay: 0,
               usingSpringWithDamping: 2.0,
               initialSpringVelocity: 10.0,
               options: [.curveEaseOut],
               animations: {
                self.view.layoutIfNeeded()
},
               completion: nil)

制約の簿記をしたり、優先順位をいじったりするよりもずっと簡単であることがわかりました。

デフォルトで非表示にするものがある場合は、ストーリーボードにレイアウトしてviewDidLoadで削除し、view.layoutIfNeeded()を使用してアニメーションなしで更新できます。

1
Adrian

@Sensefulの答えに基づいて、スタックビューをビューにラップし、彼または彼女が推奨する制約を適用するUIStackView拡張機能を次に示します。

/// wraps in a `UIView` to prevent autolayout warnings when a stack view with spacing is placed inside another stack view whose height might be zero (usually due to `hidden` being `true`).
/// See http://stackoverflow.com/questions/32428210
func wrapped() -> UIView {
    let wrapper = UIView()
    translatesAutoresizingMaskIntoConstraints = false
    wrapper.addSubview(self)

    for attribute in [NSLayoutAttribute.Top, .Left, .Right, .Bottom] {
        let constraint = NSLayoutConstraint(item: self,
                                            attribute: attribute,
                                            relatedBy: .Equal,
                                            toItem: wrapper,
                                            attribute: attribute,
                                            multiplier: 1,
                                            constant: 0)
        if attribute == .Bottom { constraint.priority = 999 }
        wrapper.addConstraint(constraint)
    }
    return wrapper
}

stackViewを追加する代わりに、stackView.wrapped()を使用します。

1
Ben Packard

スタックビュー全体を一度に非表示にしたかったのですが、OPと同じエラーが表示されていたため、修正されました。

for(UIView *currentView in self.arrangedSubviews){
    for(NSLayoutConstraint *currentConstraint in currentView.constraints){
        [currentConstraint setPriority:999];
    }
}
0
sp00ky

上記の問題の根本に対して、賢明な回答が優れた答えを提供してくれたので、すぐに解決策に進みます。

必要なことは、すべてのstackView制約の優先順位を1000未満に設定することです(999が作業を行います)。たとえば、stackViewが左、右、上、および下からそのスーパービューに制限されている場合、4つのすべての制約の優先順位は1000未満でなければなりません。

0
Linh Ta

特定のサイズクラス(例:wCompact hRegular)で作業中に制約を作成し、別のサイズクラス(例:wAny hAny)に切り替えたときに複製を作成した可能性があります。さまざまなサイズのクラスのUIオブジェクトの制約を確認し、制約に異常があるかどうかを確認します。衝突する制約を示す赤い線が表示されます。評判ポイントが10ポイントになるまで写真を撮れません:/

0
F.M

高さ制限のあるボタンの列がありました。これは、1つのボタンが非表示のときに発生します。そのボタンの高さの制約の優先度を999に設定すると、問題が解決しました。

0
Niklas