私は解決が容易ではない同様の問題を抱えていました。私の場合、スタック ビューにスタック ビューが埋め込まれていました。内部 UIStackView には 2 つのラベルがあり、ゼロ以外の間隔が指定されていました。
addArrangedSubview() を呼び出すと、次のような制約が自動的に作成されます。
V:|[innerStackView]| | = outerStackView
V:|[label1]-(2)-[label2]| | = innerStackView
innerStackView を非表示にしようとすると、あいまいな制約の警告が表示されます。
その理由を理解するために、が に等しいときにこれが起こらない理由をまず見てみましょう。を呼び出すと、@liamnichols は正しかった...は魔法のようにこの呼び出しをインターセプトし、優先度 1000 (必須)の高さUISV 非表示制約を作成します。おそらく、これは、非表示のコードがブロック内で呼び出された場合に、スタック ビュー内の要素をアニメーション化してビューの外に出せるようにするためです。残念ながら、この制約が追加されないようにする方法はないようです。それでも、次のことが発生するため、「制約を同時に満たすことができません」(USSC) 警告は表示されません。innerStackView.spacing
0
innerStackView.hidden = true
outerStackView
0
UIView.animationWithDuration()
- label1 の高さは 0 に設定されています
- 2 つのラベル間の間隔は既に 0 として定義されています
- label2 の高さは 0 に設定されています
- innerStackView の高さは 0 に設定されています
これら 4 つの制約を満たすことができることは明らかです。スタック ビューは、単純にすべてを高さ 0 のピクセルに滑らかにします。
バグのある例に戻ると、 を に設定するspacing
と2
、次の制約が適用されます。
- label1 の高さは 0 に設定されています
- 2 つのラベル間の間隔は、優先度 1000 で高さ 2 ピクセルとして、スタック ビューによって自動的に作成されました。
- label2 の高さは 0 に設定されています
- innerStackView の高さは 0 に設定されています
スタック ビューの高さを 0 ピクセルにすることも、その内容を 2 ピクセルにすることもできません。制約を満たすことができません。
注: この動作は、より単純な例で確認できます。UIView を配置されたサブビューとしてスタック ビューに追加するだけです。次に、その UIView に高さの制約を 1000 の優先度で設定します。これで hide を呼び出してみてください。
注: 何らかの理由で、これは私のスタック ビューが UICollectionViewCell または UITableViewCell のサブビューである場合にのみ発生しました。innerStackView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
ただし、内側のスタック ビューを非表示にした後で次の実行ループを呼び出すことで、セルの外側でこの動作を再現できます。
注: UIView.performWithoutAnimations でコードを実行しようとしても、スタック ビューは引き続き 0 高さ制約を追加し、USSC 警告が発生します。
この問題には、少なくとも 3 つの解決策があります。
- スタック ビューで要素を非表示にする前に、それがスタック ビューかどうかを確認し、そうである場合は
spacing
を 0に変更します。これは、コンテンツを再度表示するたびにプロセスを逆にする必要があるため (元の間隔を覚えておく必要があるため) 面倒です。
- スタック ビューで要素を非表示にする代わりに、 を呼び出します
removeFromSuperview
。プロセスを逆にすると、削除したアイテムをどこに挿入するかを覚えておく必要があるため、これはさらに面倒です。removeArrangedSubview を呼び出してから非表示にするだけで最適化できますが、まだ実行する必要がある多くの簿記があります。
- ネストされたスタック ビュー (ゼロ以外の値を持つ
spacing
) を UIView にラップします。必須でない優先度 (999 以下) として少なくとも 1 つの制約を指定します。簿記を行う必要がないため、これが最適なソリューションです。この例では、スタック ビューとラッパー ビューの間で 1000 で上、先頭、および末尾の制約を作成し、スタック ビューの下部からラッパー ビューまで 999 の制約を作成しました。このように、外側のスタック ビューが高さゼロの制約を作成すると、999 の制約が壊れ、USSC 警告が表示されなくなります。(注: これは、UICollectionViewCell サブクラスの contentView.translatesAutoResizingMaskToConstraints を に設定する必要がある場合false
の解決策に似ています)
要約すると、この動作が発生する理由は次のとおりです。
- 管理されたサブビューをスタック ビューに追加すると、Apple は自動的に 1000 の優先度制約を作成します。
- スタック ビューのサブビューを非表示にすると、高さ 0 の制約が Apple によって自動的に作成されます。
Apple が (1) 制約 (特にスペーサー) の優先順位を指定できるようにするか、(2) 自動UISV 非表示制約をオプトアウトできるようにすると、この問題は簡単に解決されます。