2

Using Xcode 10, iOS 11.4+, Swift 4

I have a series of UIViewControllers that I am navigating through, each of which is being placed into a ContainerView.

enter image description here

Based on which VC is pushed, I am hiding/showing the Top or Bottom views (shown in gray) in the Main Controller while the ContainerView is always in the middle (light blue).

What I would like to do is set the constraints so that the ContainerView is appropriately sized when the Top or Bottom views are shown/hidden.

For example, when the "Main Menu" is shown, the ContainerView should fill the entire Main Container view (Top and Bottom views will be hidden)

When "Item1" is shown, the Top view will be shown and the Bottom view hidden. Therefore, ContainerView should fill the Main Container view left, right, and bottom, but the ContainerView Top should be constrained to the Top view bottom.

When "Item5" is shown, the Top and Bottom views will also be shown. Therefore, ContainerView should fill Main Container view left, right, and be constrained to the Top view bottom, and the Bottom view top (as shown in the Main Container)

I've tried using code like this to fill the entire Main view:

NSLayoutConstraint.activate([
   ContainerView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 0),
   ContainerView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: 0),
   ContainerView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0),
   ContainerView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0)
])

However, the ContainerView never changes, and Xcode gives me a lot of warnings like:

[LayoutConstraints] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want.

Here's another screenshot from the Storyboard: enter image description here

Here's a link to download my sample project: https://gitlab.com/whoit/containerviews

How can I correctly modify the constraints to accomplish this?

wayneh
  • 4,393
  • 9
  • 35
  • 70
  • To answer this, we'll need to be know about the other constraints. Xcode already complains about to many constraints, to be satisfied simultaneously. – d4Rk Dec 23 '18 at 22:33
  • @d4Rk Sure - What else can I tell you ? I've added another screenshot... – wayneh Dec 24 '18 at 01:26
  • You need to use `UIStackView` here. Use a vertical stack view by adding **Top View**, **Main Container View** & **Bottom View** with `distribution` & `alignment` set to `Fill`. Now when you don't need any one of the three mentioned views, you just need to hide that particular view and other views will adjust their positioning. – nayem Dec 24 '18 at 04:00
  • @nayem I've tried your suggestion but I get a lot of other visual issues - you can see the first screen (fills the view) drop down to make space for the Top view that I'm unhiding. This happens even if I animate the alpha first. I don't think a stack will work. – wayneh Dec 24 '18 at 05:03
  • To be honest, I couldn't understand a single word from your last comment. What have you tried and how have you tried according to my suggestion? This is the most suitable case for using stack view. – nayem Dec 24 '18 at 05:08
  • @nayem As I mentioned I tried using a stack view (your suggestion). The problem is that prior to navigating to another view, as soon as you hide/unhide the top or bottom, the middle portion will change to fit. This change is visible. I suggest trying it based on the layout in my screenshot, or download my sample app. – wayneh Dec 24 '18 at 05:15
  • I've answered your question. I couldn't directly work on your project as I'm having a lower Xcode version currently in my machine. But I've made a demo just like your project. – nayem Dec 24 '18 at 06:36

2 Answers2

1

As I've mentioned in the comment, you should have used UIStackView for your top / bottom views visibility controlling.

You will need a UIStackView with following attributes:

  • Axis: Vertical
  • Alignment: Fill
  • Distribution: Fill
  • Spacing: As per your need

The stack view will contain the Top View, Container View and Bottom View respectively as its sub views.

You need to pin all the sides (leading, trailing, top & bottom) of this stack view to its superview (view controller's view). And as you need some height constraints for your top & bottom view, you give them as your need.


So basically your stack view is now capable of resizing its sub views when you hide any of them. You just need to perform:

theSubViewNeedsTobeHidden.isHidden = true

I've made a working demo for you that can be found here.

nayem
  • 7,285
  • 1
  • 33
  • 51
  • I've upvoted your answer even though it does not directly address my question regarding constraints. Also, FYI - I tried your sample but it contained no code for actually performing any segues. – wayneh Dec 25 '18 at 17:43
  • It's totally up to you whether you want to accept the answer or not. The procedure I described here should be enough for anyone trying to accomplish anything as you need. I just attached the demo in case you can't make it working. And there is nothing that segues have got to do with the implementation. – nayem Dec 26 '18 at 02:19
0

Issues

1) You're adding new constraints all the time, as this lines create a new constraints, each time they're getting called:

ContainerView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 0)

2) In the beginning, the MainContainerView is not constraint at all

Solution

I would suggest following:

  • add those four constraints in the storyboard instead
  • create IBOutlets for the height-constraints of your top and bottom views.
  • set the constants of those to 0 (when hiding them)
  • (optional) for sure you can still fade them in/out by setting their alpha as well, and then add the height constant to the completion block.

Note: Hidden views (alpha = 0 or isHidden = true) are still taken into account for layouting. That means, your constraints are still valid, when the views are hidden. But to make them look like hidden for the layouting as well, you'll then have to set theirs height constants to 0.

Code

@objc func hideControlViews(_ notification: Notification){
    let userInfo = notification.userInfo! as! [String : Bool]
    //print("Got top view notification: \(String(describing: userInfo["hide"]))")
    if (userInfo["hidetop"]!) {
        self.topViewHeightConstraint.constant = 0
    } else {
        self.topViewHeightConstraint.constant = 64
    }
    if (userInfo["hidebottom"]!) {
        self.bottomViewHeightConstraint.constant = 0
    } else {
        self.bottomViewHeightConstraint.constant = 108
    }

    self.view.layoutIfNeeded()
}
d4Rk
  • 6,622
  • 5
  • 46
  • 60
  • I've chosen your answer because it most directly answers my question. I'm having issues with views visibly resizing when I change the constraints and before the segue, but I think that's a result of the view structure. – wayneh Dec 25 '18 at 17:37
  • Maybe you can double check where you actually call this. E.g. before vs. after (completion block) of the dismissal. And make sure `layoutIfNeeded` is not called inside an animation block. – d4Rk Dec 26 '18 at 08:18
  • I've tried calling it from several places, but I think the issue is due to using a single VC to hold the three views. – wayneh Dec 26 '18 at 17:00