Amogam
Amogam

Reputation: 437

UIstackview how to make sure two child view have different propotional size

I have an UIStackview which has two views. I want view1 to have 70 percent of parent when axis is vertical and I want view2 to have 70 percent when axis is horizontal.

I tried to achieve this by playing horizontal hugging and resistance of two views. But nothing works.

FYI: View 1 has an image inside of it and horizontally and vertically centre to the view while width and height both is 50 View 2 is actually a label. It's not view.

Update:


If I activate and deactivate equal width/height constraints it works. But I'm getting below error

2021-02-04 20:07:15.262465+0530 test[10094:159481] [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:0x600003ccedf0 UILabel:0x7fc42ae0b1d0.width == 0.8*UIStackView:0x7fc42ae0d290.width   (active)>",
    "<NSLayoutConstraint:0x600003cce8a0 UILayoutGuide:0x6000026cc540'UIViewSafeAreaLayoutGuide'.trailing == UIStackView:0x7fc42ac0cca0.trailing   (active)>",
    "<NSLayoutConstraint:0x600003cce800 UIStackView:0x7fc42ac0cca0.leading == UILayoutGuide:0x6000026cc540'UIViewSafeAreaLayoutGuide'.leading   (active)>",
    "<NSLayoutConstraint:0x600003cc91d0 'UISV-alignment' UIView:0x7fc42ae05790.leading == UILabel:0x7fc42ae0b1d0.leading   (active)>",
    "<NSLayoutConstraint:0x600003cc9270 'UISV-alignment' UIView:0x7fc42ae05790.trailing == UILabel:0x7fc42ae0b1d0.trailing   (active)>",
    "<NSLayoutConstraint:0x600003cc9130 'UISV-canvas-connection' UIStackView:0x7fc42ae0d290.leading == UIView:0x7fc42ae05790.leading   (active)>",
    "<NSLayoutConstraint:0x600003cc9180 'UISV-canvas-connection' H:[UIView:0x7fc42ae05790]-(0)-|   (active, names: '|':UIStackView:0x7fc42ae0d290 )>",
    "<NSLayoutConstraint:0x600003cc92c0 'UISV-canvas-connection' UIStackView:0x7fc42ac0cca0.leading == UIStackView:0x7fc42ac0a850.leading   (active)>",
    "<NSLayoutConstraint:0x600003cc9310 'UISV-canvas-connection' H:[UIStackView:0x7fc42ae0d290]-(0)-|   (active, names: '|':UIStackView:0x7fc42ac0cca0 )>",
    "<NSLayoutConstraint:0x600003cc93b0 'UISV-fill-equally' UIStackView:0x7fc42ae0d290.width == UIStackView:0x7fc42ac0a850.width   (active)>",
    "<NSLayoutConstraint:0x600003cc9360 'UISV-spacing' H:[UIStackView:0x7fc42ac0a850]-(0)-[UIStackView:0x7fc42ae0d290]   (active)>",
    "<NSLayoutConstraint:0x600003cc96d0 'UIView-Encapsulated-Layout-Width' UIView:0x7fc42ac0d030.width == 414   (active)>",
    "<NSLayoutConstraint:0x600003cce990 'UIViewSafeAreaLayoutGuide-left' H:|-(0)-[UILayoutGuide:0x6000026cc540'UIViewSafeAreaLayoutGuide'](LTR)   (active, names: '|':UIView:0x7fc42ac0d030 )>",
    "<NSLayoutConstraint:0x600003cce8f0 'UIViewSafeAreaLayoutGuide-right' H:[UILayoutGuide:0x6000026cc540'UIViewSafeAreaLayoutGuide']-(0)-|(LTR)   (active, names: '|':UIView:0x7fc42ac0d030 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600003cc9270 'UISV-alignment' UIView:0x7fc42ae05790.trailing == UILabel:0x7fc42ae0b1d0.trailing   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

Upvotes: 1

Views: 2069

Answers (1)

Warren Burton
Warren Burton

Reputation: 17378

Theres no one set of constraints that will do what you want so you need to set up the constraints for both states and set those constraints isActive state true or false depending on whether your stack is in a horizontal or vertical state.

Via Storyboard

You need:

  • One proportional height constraint for the vertical case. Notice how the Installed which is the isActive property box is checked for the selected constraint. The other constraint is dimmed out as Installed/isActive is set to false.

enter image description here

  • One proportional width constraint for the horizontal case.

enter image description here

When you wish to flip, switch the isActive flag on those constraints.

class ViewController: UIViewController {

    @IBOutlet weak var stack: UIStackView!
    @IBOutlet weak var viewOne: UIView!
    @IBOutlet weak var viewTwo: UIView!
    
    @IBOutlet weak var vModeHeightConstraint: NSLayoutConstraint!
    @IBOutlet weak var hModeWidthContraint: NSLayoutConstraint!
    
    var isVertical = true {
        didSet {
            UIView.animate(withDuration: 1.0) {
                self.toggle()
            }
        }
    }
    
    func toggle() {
        vModeHeightConstraint.isActive = false
        hModeWidthContraint.isActive = false
        stack.axis = isVertical ? .vertical:.horizontal
        stack.layoutIfNeeded()
        
        vModeHeightConstraint.isActive = isVertical
        hModeWidthContraint.isActive = !isVertical
        stack.layoutIfNeeded()
    }
    
    @IBAction func toggleModeAction(_ sender: Any) {
        isVertical.toggle()
    }

}

You can set up the constraints programmatically. How to do this is easy to find on SO.

You will see a bunch of Autolayout errors during the change. Those errors can be filed under "UIKit isn't perfect".

Typically this sort of change is triggered by a device rotation in func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) but you haven't said when you want the change to occur.

Upvotes: 2

Related Questions