Auto Layout How to Hide 1 View in a View with 3 Equal Width Views

Auto layout how to hide 1 view in a view with 3 equal width views

If the deployment target is iOS9 or later, it is recommended to use UIStackView as the enclosing view. Setting isHidden to true on any view arranged in the UIStackView will not only make the view hidden, but also will the stack view re-organize all the non-hidden views to fill up the space that was taken by the hidden views. This behavior could be tuned by adjusting distribution on UIStackView.


Tj3n's answer works, but it has a drawback that you have to use magic numbers in your code and set constraint properties both in code and IB.

IMHO, a better solution would be setting up all the constraints in IB with different priorities and activating/deactivating them in code. Try this:

  1. Set the leading/trailing constraints as you did before.
  2. Set equal width constraints for all three views with priority high.
  3. Set 0 width constraints for all three views with priority required, but leave them deactivated in IB. And connect them to IBOutlets in code just as connecting views.
  4. Activate any of the 0 width constraints to collapse the view you want, and later deactivate them to expand.

Note that just with 1 and 2 in place, you can achieve the equal-width view layout. And with 3 and 4, you can collapse/expand any of the views selectively.

How to hide some of the view while all are aligned with equal width constraint?

Instead of manually setting the width constraint or setting them equal contraints each, use horizontal UIStackview with equal spacing.

Then whenever needed to hide a view, just use isHidden, UIStackview will automatically adjust the width each of the view to accomodate the space left.

Check your scenario here

import UIKit

class StackviewController : UIViewController {
let stackview: UIStackView = {
let view = UIStackView()
view.axis = .horizontal
view.distribution = .fillEqually
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()

let view1: UIView = {
let view = UIView()
view.backgroundColor = .red
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()

let view2: UIView = {
let view = UIView()
view.backgroundColor = .green
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()

let view3: UIView = {
let view = UIView()
view.backgroundColor = .blue
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()

override func viewDidLoad() {
super.viewDidLoad()

setupViews()
}

func setupViews() {
view.addSubview(stackview)

stackview.addArrangedSubview(view1)
stackview.addArrangedSubview(view2)
stackview.addArrangedSubview(view3)

NSLayoutConstraint.activate([
stackview.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8.0),
stackview.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8.0),
stackview.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0),
stackview.heightAnchor.constraint(equalToConstant: 200),
])

// view2.isHidden = true
}
}

Uncomment the line

//        view2.isHidden = true

Then you'll see the green view is gone, and other two taking the space equally.

How to use Auto Layout to move other views when a view is hidden?

It is possible, but you'll have to do a little extra work. There are a couple conceptual things to get out of the way first:

  • Hidden views, even though they don't draw, still participate in Auto Layout and usually retain their frames, leaving other related views in their places.
  • When removing a view from its superview, all related constraints are also removed from that view hierarchy.

In your case, this likely means:

  • If you set your left view to be hidden, the labels stay in place, since that left view is still taking up space (even though it's not visible).
  • If you remove your left view, your labels will probably be left ambiguously constrained, since you no longer have constraints for your labels' left edges.

What you need to do is judiciously over-constrain your labels. Leave your existing constraints (10pts space to the other view) alone, but add another constraint: make your labels' left edges 10pts away from their superview's left edge with a non-required priority (the default high priority will probably work well).

Then, when you want them to move left, remove the left view altogether. The mandatory 10pt constraint to the left view will disappear along with the view it relates to, and you'll be left with just a high-priority constraint that the labels be 10pts away from their superview. On the next layout pass, this should cause them to expand left until they fill the width of the superview but for your spacing around the edges.

One important caveat: if you ever want your left view back in the picture, not only do you have to add it back into the view hierarchy, but you also have to reestablish all its constraints at the same time. This means you need a way to put your 10pt spacing constraint between the view and its labels back whenever that view is shown again.

AutoLayout with hidden UIViews?

UIStackView is probably the way to go for iOS 9+. Not only does it handle the hidden view, it will also remove additional spacing and margins if set up correctly.

AutoLayout : Relative scaling of views

I guess you can use UIView that contains a button inside and set bottom,lead,trailing,top constraints for the UIView. Then you can set height constraint of that UIView and set multiplier values to have relative height to its superView(=Red box). Finally, you can set constraints for your button inside the UIView whatever you want. If you know how to use StackView, I recommend you to use it because it is easy and simple solution. Here is very good tutorial about StackView.

Evenly space multiple views within a container view

So my approach allows you to do this in interface builder. What you do is create 'spacer views' that you have set to match heights equally. Then add top and bottom constraints to the labels (see the screenshot).

Sample Image

More specifically, I have a top constraint on 'Spacer View 1' to superview with a height constraint of lower priority than 1000 and with Height Equals to all of the other 'spacer views'. 'Spacer View 4' has a bottom space constraint to superview. Each label has a respective top and bottom constraints to its nearest 'spacer views'.

Note: Be sure you DON'T have extra top/bottom space constraints on your labels to superview; just the ones to the 'space views'. This will be satisfiable since the top and bottom constraints are on 'Space View 1' and 'Spacer View 4' respectively.

Duh 1: I duplicated my view and merely put it in landscape mode so you could see that it worked.

Duh 2: The 'spacer views' could have been transparent.

Duh 3: This approach could be applied horizontally.



Related Topics



Leave a reply



Submit