2015-10-12 15 views
19

ネストされたUIStackViewを使用して、Interface Builderで構築された複雑なビュー階層を持っています。私は内部のスタックビューのいくつかを隠すたびに「満たされない制約」の通知を受け取ります。 、具体的にネストされたUIStackViews壊れた制約

(
    "<NSLayoutConstraint:0x1396632d0 'UISV-canvas-connection' UIStackView:0x1392c5020.top == UILabel:0x13960cd30'Also available on iBooks'.top>", 
    "<NSLayoutConstraint:0x139663470 'UISV-canvas-connection' V:[UIButton:0x139554f80]-(0)-| (Names: '|':UIStackView:0x1392c5020)>", 
    "<NSLayoutConstraint:0x139552350 'UISV-hiding' V:[UIStackView:0x1392c5020(0)]>", 
    "<NSLayoutConstraint:0x139663890 'UISV-spacing' V:[UILabel:0x13960cd30'Also available on iBooks']-(8)-[UIButton:0x139554f80]>" 
) 

UISV-spacing制約:UIStackViewを隠したときに、その高い制約が0の定数を取得しますが、それは、内側stackviewの間隔制約と衝突するようだ:それは8ポイントを必要とし、私はこれにそれを追跡してきました私のラベルとボタンとの間には、隠れ制約とは相容れないので、制約がクラッシュします。

方法はありますか?再帰的に隠しスタックビューの内側のStackViewをすべて隠してみましたが、コンテンツが画面から浮き上がって奇妙なアニメーションが発生し、重大なFPSが起動して問題を解決しません。

答えて

11

理想的には、UISV-spacing制約の優先度を低い値に設定することができますが、それを行う方法はありません。 :)

ネストされたスタックビューのspacingプロパティを非表示にする前に0に設定し、再度表示した後に適切な値に復元するのに成功しています。

入れ子になったスタックビューでこれを再帰的に実行するとうまくいくと思います。 spacingプロパティの元の値を辞書に格納し、後で復元することができます。

私のプロジェクトには1つのレベルの入れ子しかないので、FPSの問題が発生するかどうかはわかりません。スペーシングの変化をアニメーション化しない限り、あまりにも多くのヒットを生み出すとは思わない。

+1

おかげで、今日はそれを試してみるとそれが動作するかどうかの答えを受け入れるだろう:) –

12

私はUISV隠れ同様の問題に遭遇しました。私の場合、解決策は自分の制約の優先順位をRequired(1000)からそれ以下のものに減らすことでした。 UISV非表示の制約が追加されると、制約が優先され、制約はもはや衝突しません。

+2

問題は、私はこれらの要素の上に私自身のない制約がないです:(しかし、これは私の場合には、他の同様の問題を解決しました。 –

+0

は、私は同様の問題があったし、これは私のためにそれを修正しました。 – SeanR

+0

聖なる牛を!、熟考不思議、といくつかの深刻なブレーンストーミングの一日を過ごした後、これはそれだったやった。それはUIStackViewたちはドン制約に動作するという意味があります。!トン参照が、私たちの制約と本質的に競合グレート答えを、@Jaanusありがとう – kbpontius

13

これは、ネストされたスタックビューを隠す際の既知の問題です。

は、基本的にこの問題には3つのソリューションがあります。

  1. 変更0までの間隔が、その後、あなたは、以前の間隔値を覚えておく必要があります。
  2. innerStackView.removeFromSuperview()を呼び出してください。スタックビューを挿入する場所を覚えておく必要があります。
  3. 少なくとも1つの999制約を持つUIViewでスタックビューをラップします。例えば。 @ 1000、@ 1000、@ 999を先頭に1000 @。

第3のオプションは、私の意見では最高です。この問題の詳細とその原因、さまざまなソリューション、およびソリューション3の実装方法については、my answer to a similar questionを参照してください。

+1

ソリューション3は私のために働いた、私はすべてのエッジ(通常のような)に制限されたUIView内のスタックビューを配置します。それは、スタックビューはルートビューのようにdoesntのようです。 – malhal

0

SnapKitの制約を使用してSwift 3クラスとして書かれたSensefulの提案#3の実装です。私はまた、プロパティをオーバーライドしようとしたが、それは警告なしに働いてやったことがなかったので、私はUIStackViewをラップに固執ます:

class NestableStackView: UIView { 
    private var actualStackView = UIStackView() 

    override init(frame: CGRect) { 
     super.init(frame: frame); 
     addSubview(actualStackView); 
     actualStackView.snp.makeConstraints { (make) in 
      // Lower edges priority to allow hiding when spacing > 0 
      make.edges.equalToSuperview().priority(999); 
     } 
    } 

    convenience init() { 
     self.init(frame: CGRect.zero); 
    } 

    required init?(coder aDecoder: NSCoder) { 
     fatalError("init(coder:) has not been implemented") 
    } 

    func addArrangedSubview(_ view: UIView) { 
     actualStackView.addArrangedSubview(view); 
    } 

    func removeArrangedSubview(_ view: UIView) { 
     actualStackView.removeArrangedSubview(view); 
    } 

    var axis: UILayoutConstraintAxis { 
     get { 
      return actualStackView.axis; 
     } 
     set { 
      actualStackView.axis = newValue; 
     } 
    } 

    open var distribution: UIStackViewDistribution { 
     get { 
      return actualStackView.distribution; 
     } 
     set { 
      actualStackView.distribution = newValue; 
     } 
    } 

    var alignment: UIStackViewAlignment { 
     get { 
      return actualStackView.alignment; 
     } 
     set { 
      actualStackView.alignment = newValue; 
     } 
    } 

    var spacing: CGFloat { 
     get { 
      return actualStackView.spacing; 
     } 
     set { 
      actualStackView.spacing = newValue; 
     } 
    } 
} 
7

だから、あなたはこの持っている:

broken animation

をし、問題がありますあなたが最初の内側のスタックを折りたたむ際に、あなたは、自動レイアウトエラーを取得:

2017-07-02 15:40:02.377297-0500 nestedStackViews[17331:1727436] [LayoutConstraints] 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. 
(
    "<NSLayoutConstraint:0x62800008ce90 'UISV-canvas-connection' UIStackView:0x7fa57a70fce0.top == UILabel:0x7fa57a70ffb0'Top Label of Inner Stack'.top (active)>", 
    "<NSLayoutConstraint:0x62800008cf30 'UISV-canvas-connection' V:[UILabel:0x7fa57d30def0'Bottom Label of Inner Sta...']-(0)-| (active, names: '|':UIStackView:0x7fa57a70fce0)>", 
    "<NSLayoutConstraint:0x62000008bc70 'UISV-hiding' UIStackView:0x7fa57a70fce0.height == 0 (active)>", 
    "<NSLayoutConstraint:0x62800008cf80 'UISV-spacing' V:[UILabel:0x7fa57a70ffb0'Top Label of Inner Stack']-(8)-[UILabel:0x7fa57d30def0'Bottom Label of Inner Sta...'] (active)>" 
) 

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x62800008cf80 'UISV-spacing' V:[UILabel:0x7fa57a70ffb0'Top Label of Inner Stack']-(8)-[UILabel:0x7fa57d30def0'Bottom Label of Inner Sta...'] (active)> 

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. 

問題を、あなたが述べたように、外側のスタック表示がheigを適用するということですht = 0の制約が内側のスタックビューに適用されます。これは、内部スタックビューによって自身のサブビュー間で適用される8ポイントのパディング制約と競合します。両方の制約を同時に満たすことはできません。

外側のスタックビューではこの高さ= 0の制約が使用されています。最初に収縮させずに内側のビューを隠すだけでなく、アニメーション化するとより見栄えがよいからです。

これには簡単な修正があります:UIViewというプレーンで内側のスタックビューをラップし、そのラッパーを非表示にします。私は実証します。

は、ここでは上記の壊れたバージョンのためのシーンの概要です:

broken outline

内部スタックビューを選択し、問題を解決するには。メニューバーから、編集>埋め込みで>ビューを選択します。

embed in view

Interface Builderは、私はこれをしなかったときに、ラッパービューの幅の制約を作成したので、その幅の制約を削除します。

delete width constraint

create constraints

次に、ラッパーのすべての4つの縁部と内側スタックビュー間の制約を作成します

この時点では、レイアウトは実際には実行時には正しいものの、Interface Builderはそれを正しく描画しません。内側のスタックの子の垂直方向の抱き合わせの優先順位を高く設定することで修正できます。私は800にそれらを設定します。

hugging priorities

私たちは、実際にこの時点で充足不能制約の問題を修正していません。これを行うには、作成した下部制約を見つけ、その優先順位を必要以上に低く設定します。のが800に変更してみましょう:あなたはそのhiddenプロパティを変更したため

change bottom constraint priority

は最後に、あなたはおそらく、内部スタックビューに接続され、あなたのビューコントローラでアウトレットを持っていました。そのコンセントを変更して、内側のスタックビューの代わりにラッパービューに接続します。コンセントのタイプがUIStackViewの場合は、UIViewに変更する必要があります。鉱山は、すでにタイプUIViewのだったので、私はちょうどストーリーボードでそれを再接続:あなたはラッパービューのhiddenプロパティを切り替えるとき

change outlet

は今、スタック表示が無い充足不能制約の警告で、崩壊して表示されます。それは実質的に同じように見えるので、私はアプリの別のGIFを掲載している気にしません。

あなたは私のテストプロジェクトin this github repositoryを見つけることができます。

+0

ラッパービューを使用してアニメーション効果を簡単に変更できることにも注意してください。ラッパービューの "Clips To Bounds"をオンにし、ボトムコンストレイントの優先度を600に設定してみてください。スキッキングエフェクトの代わりに素敵なスライドアンダーエフェクトが得られます。 –

0

別のアプローチ

ネストされたUIStackViewsを避けるようにしてください。私はそれらを愛し、それらとほとんどすべてを構築する。しかし、彼らが密かに制約を加えていることを認識したので、私はそれらを最高レベルで使用し、可能な限り入れ子にしないようにしています。このようにして、私の警告を解決するスペース制約に第2位の優先度.defaultHighを指定することができます。

この優先順位は、ほとんどのレイアウトの問題を防ぐのにちょうど十分です。もちろん

あなたはいくつかのより多くの制約を指定する必要がありますが、この方法は、あなたはそれらの完全な制御を持っているし、あなたのビューのレイアウトを明示的に。

関連する問題