web-dev-qa-db-ja.com

ビューが非表示のときに自動レイアウトを使用して他のビューを移動する方法

私はカスタムCellをIBで設計し、それをサブクラス化し、そして私のアウトレットを私のカスタムクラスに接続しました。 UIView(cdView)と2つのラベル(titleLabelおよびemailLabel)というセルコンテンツに3つのサブビューがあります。各行で利用できるデータによっては、UIViewと2つのラベルを自分のセルに表示したい場合と、2つのラベルだけを表示したい場合があります。 UIViewプロパティを非表示に設定したり、スーパービューから削除したりすると、2つのラベルが左に移動します。次のビュー(UIView)には、UIViewの先頭の制約を10pxのスーパービュー(セルの内容)に設定し、UILabelsの先頭に10pxの制約を設定しようとしました。私のコードの後半

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(IndexPath *)indexPath {
...
Record *record = [self.records objectAtIndex:indexPath.row];

if ([record.imageURL is equalToString:@""]) {
     cell.cdView.hidden = YES;
}

私は自分のcell.cdViewを隠していて、ラベルを左に移動したいのですが、それらはCellの同じ位置に留まっています。私はsuperviewからcell.cdViewを削除しようとしましたが、それはどちらもうまくいきませんでした。私は自分が何をしているのかを明確にするために画像を添付しました。

cell

私はプログラム的にこれを行う方法を知っています、そして私はその解決策を探していません。私が欲しいのはIBで制約を設定することです、そして私は私のサブビューが他のビューが削除されるか隠されるなら動的に動くと期待します。自動レイアウトを使用してIBでこれを行うことは可能ですか?

.....
305
Guferos

それは可能ですが、あなたはもう少し余分な仕事をしなければならないでしょう。最初に邪魔にならないようにするための概念的なことが2つあります。

  • 非表示のビュー、たとえ描画していなくても、Auto Layoutではまだ参加しています、通常はフレームを保持していますで、他の関連ビューはその場所に残ります。
  • スーパービューからビューを削除すると、すべての関連制約もそのビュー階層から削除されます。

あなたの場合、これはおそらく以下のことを意味します。

  • 左側のビューを非表示にするように設定した場合、左側のビューはまだ表示されていないにもかかわらずスペースを占有しているため、ラベルはそのまま残ります。
  • 左側のビューを削除すると、ラベルの左端に制約がなくなるため、ラベルはおそらくあいまいに制約されたままになります。

あなたがする必要があるのはあなたのラベルを慎重に過度に制約するです。既存の制約(他のビューへの10ポイントのスペース)をそのままにして、別の制約を追加します。ラベルの左端をスーパービューの左エッジから10ポイント離して不必要な優先度で設定します。

次に、それらを左に移動させたいときは、左側のビューを完全に削除します。左側のビューに対する必須の10pt制約は、それが関連するビューと一緒に表示されなくなり、ラベルがスーパービューから10pt離れているという優先順位の高い制約だけが残ります。次のレイアウトパスでは、スーパービューの幅いっぱいになるまで左に拡大しますが、端の周囲にはスペースが必要です。

1つの重要な注意点:もしあなたがあなたの左のビューを図の中に戻したいのなら、それをビューの階層に追加し直さなければならないだけでなく、同時に{そのすべての制約を再確立するこれは、そのビューが再び表示されるたびに、ビューとそのラベルの間の10ポイントの間隔の制約を元に戻す方法が必要であることを意味します。

351
Tim

実行時に制約を追加または削除することは、パフォーマンスに影響を与える可能性がある重い操作です。しかし、もっと簡単な方法があります。

非表示にしたいビューに対して、幅の制限を設定します。他のビューを、そのビューに対して水平方向の間隔を空けて拘束します。

非表示にするには、width制約の.constantを0.fに更新します。他のビューは自動的に左に移動して位置を決めます。

詳細については私の他の答えをここに見なさい:

実行時にラベルの制約を変更するにはどうすればいいですか?

199
Max MacLeod

iOS 8 +のみをサポートする人のために、新しいブール型プロパティ active があります。必要な制約だけを動的に有効にするのに役立ちます

P.S拘束アウトレットは強い、弱いではない

例:

@IBOutlet weak var optionalView: UIView!
@IBOutlet var viewIsVisibleConstraint: NSLayoutConstraint!
@IBOutlet var viewIsHiddenConstraint: NSLayoutConstraint!

func showView() {
    optionalView.isHidden = false
    viewIsVisibleConstraint.isActive = true
    viewIsHiddenConstraint.isActive = false
}

func hideView() {
    optionalView.isHidden = true
    viewIsVisibleConstraint.isActive = false
    viewIsHiddenConstraint.isActive = true
}

ストーリーボードのエラーを修正するには、これらの制約の1つについてInstalledチェックボックスをオフにする必要があります。

UIStackView(iOS 9以降)
もう1つの選択肢は、あなたの見解をUIStackViewでラップすることです。ビューが隠されるとUIStackViewは自動的にレイアウトを更新します

77
Silmaril

いずれかのサブビューでUIStackViewプロパティが変更されると、hiddenは自動的にビューの位置を変更します(iOS 9以降)。

UIView.animateWithDuration(1.0) { () -> Void in
   self.mySubview.hidden = !self.mySubview.hidden
}

デモを見るには、このWWDCビデオの 11:48 に移動します。

自動レイアウトの謎、パート1

56
cosine

私のプロジェクトはUILabelのカスタム@IBDesignableサブクラスを使い(色、フォント、インセットなどの一貫性を保つため)、私は次のようなものを実装しました:

override func intrinsicContentSize() -> CGSize {
    if hidden {
        return CGSizeZero
    } else {
        return super.intrinsicContentSize()
    }
}

これにより、ラベルサブクラスは自動レイアウトに参加できますが、非表示の場合はスペースを取りません。

16
Robert Atkins

Google社員向け:Maxの答えを基に、多くの人が気付いたパディングの問題を解決するために、単にラベルの高さを増やして実際のパディングではなく区切り文字。この考えは、ビューを含むことで、どんなシナリオにも拡張できます。

これは簡単な例です:

IB Screenshot

この場合、Authorラベルの高さを適切なIBOutletにマッピングします。

@property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight;

制約の高さを0.0fに設定すると、Playボタンの高さで許容されるため、 "padding"が維持されます。

13
jterry

私がやってしまったのは2 xibを作成することでした。 1つは左側のビュー、もう1つはそれなしです。両方ともコントローラーに登録してから、cellForRowAtIndexPathの中でどちらを使用するかを決めました。

それらは同じUITableViewCellクラスを使用します。欠点は、xib間でコンテンツの重複があることですが、これらのセルはかなり基本的なものです。利点は、ビューの削除、制約の更新などを手動で管理するためのコードがないことです。

一般的に、これは技術的に異なるレイアウトであり、したがって異なるxibを持つべきであるので、おそらくより良い解決策です。

[self.table registerNib:[UINib nibWithNibName:@"TrackCell" bundle:nil] forCellReuseIdentifier:@"TrackCell"];
[self.table registerNib:[UINib nibWithNibName:@"TrackCellNoImage" bundle:nil] forCellReuseIdentifier:@"TrackCellNoImage"];

TrackCell *cell = [tableView dequeueReusableCellWithIdentifier:(appDelegate.showImages ? @"TrackCell" : @"TrackCellNoImage") forIndexPath:indexPath];
8

uiviewとラベル間の制約をIBOutletとして接続し、hidden = YESに設定されている場合は優先順位メンバーをより小さい値に設定

8
n0c

この場合、著者ラベルの高さを適切なIBOutletにマッピングします。

@property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight;

また、拘束の高さを0.0fに設定しても、[再生]ボタンの高さで許容されるため、 "パディング"は維持されます。

cell.authorLabelHeight.constant = 0;

enter image description hereenter image description here

6
Mitul Marsoniya

これを試してください、私は以下のコードを実装しました、

私は他の3つのビューを追加したという点でViewControllerで1つのビューを持っています。 、

1.ViewController.hファイル

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIView *viewOne;
@property (strong, nonatomic) IBOutlet UIView *viewTwo;
@property (strong, nonatomic) IBOutlet UIView *viewThree;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewOneWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewTwoWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewThreeWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewBottomWidth;
@end

2.ViewController.m

 #import "ViewController.h"
 @interface ViewController ()
{
  CGFloat viewOneWidthConstant;
  CGFloat viewTwoWidthConstant;
  CGFloat viewThreeWidthConstant;
  CGFloat viewBottomWidthConstant;
}
@end

@implementation ViewController
@synthesize viewOne, viewTwo, viewThree;

- (void)viewDidLoad {
  [super viewDidLoad];
 // Do any additional setup after loading the view, typically from a 
  nib.

  /*
   0  0   0
   0  0   1
   0  1   0
   0  1   1
   1  0   0
   1  0   1
   1  1   0
   1  1   1
   */

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:NO];

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:YES];

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:YES];
  //    [viewThree setHidden:NO];

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:YES];
  //    [viewThree setHidden:YES];


  //    [viewOne setHidden:YES];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:NO];

  //    [viewOne setHidden:YES];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:YES];

 //    [viewOne setHidden:YES];
 //    [viewTwo setHidden:YES];
 //    [viewThree setHidden:NO];

//    [viewOne setHidden:YES];
//    [viewTwo setHidden:YES];
//    [viewThree setHidden:YES];

 [self hideShowBottomBar];
  }

- (void)hideShowBottomBar
{
  BOOL isOne = !viewOne.isHidden;
  BOOL isTwo = !viewTwo.isHidden;
  BOOL isThree = !viewThree.isHidden;

  viewOneWidthConstant = _viewOneWidth.constant;
  viewTwoWidthConstant = _viewTwoWidth.constant;
  viewThreeWidthConstant = _viewThreeWidth.constant;
  viewBottomWidthConstant = _viewBottomWidth.constant;

   if (isOne && isTwo && isThree) {
    // 0    0   0
    _viewOneWidth.constant = viewBottomWidthConstant / 3;
    _viewTwoWidth.constant = viewBottomWidthConstant / 3;
    _viewThreeWidth.constant = viewBottomWidthConstant / 3;
    }
    else if (isOne && isTwo && !isThree) {
     // 0    0   1
    _viewOneWidth.constant = viewBottomWidthConstant / 2;
    _viewTwoWidth.constant = viewBottomWidthConstant / 2;
    _viewThreeWidth.constant = 0;
    }
   else if (isOne && !isTwo && isThree) {
    // 0    1   0
    _viewOneWidth.constant = viewBottomWidthConstant / 2;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = viewBottomWidthConstant / 2;
    }
    else if (isOne && !isTwo && !isThree) {
    // 0    1   1
    _viewOneWidth.constant = viewBottomWidthConstant;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = 0;
   }
   else if (!isOne && isTwo && isThree) {
    // 1    0   0
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = viewBottomWidthConstant / 2;
    _viewThreeWidth.constant = viewBottomWidthConstant / 2;
   }
   else if (!isOne && isTwo && !isThree) {
    // 1    0   1
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = viewBottomWidthConstant;
    _viewThreeWidth.constant = 0;
   }
   else if (!isOne && !isTwo && isThree) {
    // 1    1   0
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = viewBottomWidthConstant;
   }
   else if (isOne && isTwo && isThree) {
    // 1    1   1
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = 0;
   }
  }

 - (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
 // Dispose of any resources that can be recreated.
 }
 @end

enter image description hereenter image description hereenter image description here

それでこの論理が誰かに役立つことを願っています。

2
Jaywant Khedkar

私の場合は、高さ制約の定数を0.0fに設定し、またhiddenプロパティをYESに設定します。

ビューを(サブビュー付きで)再度表示するには、反対の操作を行いました。高さ定数をゼロ以外の値に設定し、hiddenプロパティをNOに設定します。

1
testing

これは私のもう一つの解決策で、優先順位制約を使用しています。アイデアは幅を0に設定しています。

  1. コンテナビュー(オレンジ色)を作成して幅を設定します。 enter image description here

  2. コンテンツビュー(赤)を作成し、末尾のスペース10ptをスーパービュー(オレンジ)に設定します。末尾のスペースの制約に注意してください。優先度が異なる末尾の制約が2つあります。低(= 10)および高(<= 10)。これはあいまいさを避けるために重要です。 enter image description here

  3. ビューを非表示にするには、オレンジ色のビューの幅を0に設定します。 enter image description here

1
mnemonic23

水平スタックビューを使用します。サブビューが非表示になったときにフレームを削除できます。

下の画像では、赤いビューがコンテンツの実際のコンテナで、オレンジのスーパービュー(ShowHideView)の後ろに10ptのスペースがあります。次に、ShowHideViewをIBOutletに接続して、プログラムで表示/非表示/削除するだけです。

  1. これは、ビューが表示/インストールされているときです。

view is visible

  1. これは、ビューが非表示/未インストールの場合です。

view is hidden/removed

1
mnemonic23

No_sceneが示唆しているように、実行時に制約の優先順位を変更することでこれを確実に行うことができます。削除しなければならないビューが複数あるため、これは私にとってはずっと簡単でした。

これがReactiveCocoaを使ったスニペットです。

RACSignal* isViewOneHiddenSignal = RACObserve(self.viewModel, isViewOneHidden);
RACSignal* isViewTwoHiddenSignal = RACObserve(self.viewModel, isViewTwoHidden);
RACSignal* isViewThreeHiddenSignal = RACObserve(self.viewModel, isViewThreeHidden);
RAC(self.viewOne, hidden) = isViewOneHiddenSignal;
RAC(self.viewTwo, hidden) = isViewTwoHiddenSignal;
RAC(self.viewThree, hidden) = isViewThreeHiddenSignal;

RAC(self.viewFourBottomConstraint, priority) = [[[[RACSignal
    combineLatest:@[isViewOneHiddenSignal,
                    isViewTwoHiddenSignal,
                    isViewThreeHiddenSignal]]
    and]
    distinctUntilChanged]
    map:^id(NSNumber* allAreHidden) {
        return [allAreHidden boolValue] ? @(780) : @(UILayoutPriorityDefaultHigh);
    }];

RACSignal* updateFramesSignal = [RACObserve(self.viewFourBottomConstraint, priority) distinctUntilChanged];
[updateFramesSignal
    subscribeNext:^(id x) {
        @strongify(self);
        [self.view setNeedsUpdateConstraints];
        [UIView animateWithDuration:0.3 animations:^{
            [self.view layoutIfNeeded];
        }];
    }];
0
skensell

私はこれが最も簡単な答えであることそれがうまくいっていることのためにそれをチェックしてください

        StackFullView.layer.isHidden = true
        Task_TopSpaceSections.constant = 0.   //your constrain of top view

ここでチェック https://www.youtube.com/watch?v=EBulMWMoFuw

0
Gowthaman M

あなたの解決策を得るために私が私のuiviewsを再調整する方法は次のとおりです。

  1. 1つのUIImageViewをドラッグアンドドロップして左側に配置します。
  2. 1つのUIViewをドラッグアンドドロップして、UIImageViewの右側に配置します。
  3. そのUIViewの中に2つのUILabelをドラッグアンドドロップします。
  4. 2つのラベルを含むUIViewの先頭の制約をUIImagViewではなくsuperviewに設定します。
  5. UIImageViewが非表示になっている場合は、スーパービューに対して先行拘束定数を10ピクセルに設定します。 ELSEでは、先行拘束定数を10 px + UIImageView.width + 10 pxに設定します。

私は自分自身のサムルールを作成しました。制約が影響を受ける可能性のあるすべてのuiviewを非表示または表示する必要があるときはいつでも、uiview内にすべての影響を受ける/依存するサブビューを追加し、その先頭/末尾/上/下制約をプログラムで更新します。

0
Deepak Thakur

これが誰かに役立つ場合には、 視覚フォーマット の制約を使うためのヘルパークラスを作りました。現在のアプリで使用しています。

AutolayoutHelper

それは私のニーズに少し合わせたものかもしれませんが、あなたはそれが便利だと思うかもしれませんし、あるいはあなたがそれを修正してあなた自身のヘルパーを作りたいと思うかもしれません。

私はTimに上記の 答え 、この UIScrollViewについての答え を感謝しなければなりませんまた、この チュートリアル もあります。

0
Ferran Maylinch

これを行う適切な方法は、isActive = falseで制約を無効にすることです。ただし、制約を無効にすると制約が削除されて解放されるため、強力なコンセントを用意する必要があります。

0
Hogdotmac

これは古い質問ですが、それでも役に立つことを願っています。 Androidから来たこのプラットフォームでは、ビューからそれを隠すための便利なメソッドisVisibleを持っていますが、自動レイアウトがビューを描くときにフレームを考慮していません。

extensionと "extend"というuiviewを使えば、iosでも似たような機能を実行することができます(なぜUIKitに含まれていないのかわからない)、ここでSwift 3の実装です。

    func isVisible(_ isVisible: Bool) {
        self.isHidden = !isVisible
        self.translatesAutoresizingMaskIntoConstraints = isVisible
        if isVisible { //if visible we remove the hight constraint 
            if let constraint = (self.constraints.filter{$0.firstAttribute == .height}.first){
                self.removeConstraint(constraint)
            }
        } else { //if not visible we add a constraint to force the view to have a hight set to 0
            let height = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal , toItem: nil, attribute: .notAnAttribute, multiplier: 0, constant: 0)
            self.addConstraint(height)
        }
        self.layoutIfNeeded()
    }
0