1

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.
Warren Burton
  • 17,451
  • 3
  • 53
  • 73
Amogam
  • 321
  • 5
  • 20
  • 1
    You need to set up the constraints for each case and set them inactive/active when you decide to change from horizontal to vertical. – Warren Burton Feb 03 '21 at 22:21
  • Also, you can add constraints for 70% by setting a multiplier value to the width constraint. But yeah, like Warren said, you'll need to activate/deactivate constraints when the orientation changes as well. – johnny Feb 03 '21 at 22:24
  • @WarrenBurton - I can change the axis of stack view it would easily change vertical to horizontal – Amogam Feb 04 '21 at 01:59
  • @Amogam - are you switching the axis between Vertical and Horizontal based on trait change? That is, you want Vertical axis for Regular Height and Horizontal for Compact Height? Or some other reason? And, are you designing in Storyboard, or just creating the stack view and subviews via code? – DonMag Feb 04 '21 at 13:04
  • @DonMag - I am doing the axis change based on business logic. I'm using storyboard – Amogam Feb 04 '21 at 14:01

1 Answers1

2

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.

Warren Burton
  • 17,451
  • 3
  • 53
  • 73