How to create percentage of total width using autolayout?

IosXcodeAutolayout

Ios Problem Overview


I need to create three dynamic columns, each with a fixed percentage of the total width. Not thirds, but different values. For example, the following illustration shows three columns: the first being 42% wide, the second being 25% wide, and the third being 33% wide.

For a 600 pixel across viewcontroller, that would be 252, 150, and 198 pixels respectively.

However, for any subsequent display sizes (i.e. iPhone 4 landscape (960 wide) or iPad 2 portrait (768 wide), I would like the relative percentages to be the same (not the pixel widths quoted above).

Is there a way to do this using Storyboards (i.e. without code)? I can do this easily in code, but my goal is to put as much of this display logic as possible into the Storyboard.

enter image description here

Ios Solutions


Solution 1 - Ios

If, as you say, you know how to do it in code, then you already know how to do it in the storyboard. It's exactly the same constraints, but you are creating them visually rather than in code.

  1. Select both a view and its superview.

  2. Choose Editor -> Pin -> Widths Equally to constrain the width to be equal to the superview's width (actually the "pin" popup dialog at the bottom of the canvas works best here).

  3. Edit the constraint and set the Multiplier to the desired fraction, e.g. 0.42. And so too for the other views.

Solution 2 - Ios

As Apple introduces UIStackView it made job much easy.

Method 1: Using Nib/StoryBoard:

You have to just add three view in interface builder & embed them into stackview

> Xcode ► Editor ► Embed in ► StackView

enter image description here

Select stackView & give constraint with leading, trailing, top & equal height with safeArea

Click to Attribute inspector area & Set StackView horizontal & distribution to fill proportionally

[enter image description here3

Give constraint of three view with leading, trailing, top, bottom with respective of sides. enter image description here

Method 2: Programmatically:

import UIKit
class StackViewProgramatically: UIViewController {
    var propotionalStackView: UIStackView!
    ///Initially defining three views
    let redView: UIView = {
        let view = UIView()//taking 42 % initially
        view.frame = CGRect(x: 0, y: 0, width: 42 * UIScreen.main.bounds.width/100, height: UIScreen.main.bounds.height)
        view.backgroundColor = .red
        return view
    }()

    let greenView: UIView = {
        let view = UIView()//taking 42* initially
        view.frame = CGRect(x: 42 * UIScreen.main.bounds.width/100, y: 0, width: 25 * UIScreen.main.bounds.width/100, height: UIScreen.main.bounds.height)
        view.backgroundColor = .green
        return view
    }()
    let blueView: UIView = {
        let view = UIView()//taking 33*initially
        view.frame = CGRect(x: 67 * UIScreen.main.bounds.width/100, y: 0, width: 33 * UIScreen.main.bounds.width/100, height: UIScreen.main.bounds.height)
        view.backgroundColor = .blue
        return view
    }()

    ///Changing UIView frame to supports landscape mode.
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        DispatchQueue.main.async {
            self.redView.frame = CGRect(x: 0, y: 0, width: 42 * self.widthPercent, height: self.screenHeight)
            self.greenView.frame = CGRect(x: 42 * self.widthPercent, y: 0, width: 25 * self.widthPercent, height: self.screenHeight)
            self.blueView.frame = CGRect(x: 67 * self.widthPercent, y: 0, width: 33 * self.widthPercent, height: self.screenHeight)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        //Adding subViews to the stackView
        propotionalStackView = UIStackView()
        propotionalStackView.addSubview(redView)
        propotionalStackView.addSubview(greenView)
        propotionalStackView.addSubview(blueView)
        propotionalStackView.spacing = 0
        ///setting up stackView
        propotionalStackView.axis = .horizontal
        propotionalStackView.distribution = .fillProportionally
        propotionalStackView.alignment = .fill
        view.addSubview(propotionalStackView)
    }
}
//MARK: UIscreen helper extension
extension NSObject {

    var widthPercent: CGFloat {
        return UIScreen.main.bounds.width/100
    }
    
    var screenHeight: CGFloat {
        return UIScreen.main.bounds.height
    }
}

Output:

Works with landscape & portrait

enter image description here enter image description here

Demo project - https://github.com/janeshsutharios/UIStackView-with-constraints

https://developer.apple.com/videos/play/wwdc2015/218/

Solution 3 - Ios

I think this can be explained in more detail so it can be more easily applied to any number of views requiring fixed percentage layouts within a superview.

Left-most view
  • Anchored to SuperView.Leading
  • Defines its fixed percentage as a multiplier on the SuperView.Height
Intermediate views
  • Defines its fixed percentage as a multiplier on the SuperView.Height
  • Pins its left to its neighbor's right
Right-Most view
  • Does not define a fixed percentage (it is the remainder of the available view)
  • Pins its left to its neighbor's right
  • Pins its right to SuperView.Trailing
All Views
  • Define their non-fixed heights by anchoring to Top Layout Guide.Top and Top Layout Guide.bottom. In the answer above, it is noted that this can also be done by setting equal height to the neighboring view.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJeffrey BerthiaumeView Question on Stackoverflow
Solution 1 - IosmattView Answer on Stackoverflow
Solution 2 - IosJackView Answer on Stackoverflow
Solution 3 - IosJuJoDiView Answer on Stackoverflow