UIStackview align left - ios

I'm trying to align the items in UIStackview horizontally to the left.
Is there anyway to do this programmatically? I tried to do this via storyboard but was not successful. For some reason, the items in the UIStackView centers itself.

If you use StackView setting axis property to "Horizontal",
I think you can't align it to the left.
But there is a way to make it left visually.
set StackView leading constraint with "equal" relationship
set StackView trailing constraint with "greater than or equal"
like this
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(greaterThanOrEqualTo: view.trailingAnchor).isActive = true

Try Adding One Space View in Your Stack View , Like this
// To Make Our Buttons aligned to left We have added one spacer view
UIButton *spacerButton = [[UIButton alloc] init];
[spacerButton setContentHuggingPriority:UILayoutPriorityFittingSizeLevel forAxis:UILayoutConstraintAxisHorizontal];
[spacerButton setContentCompressionResistancePriority:UILayoutPriorityFittingSizeLevel forAxis:UILayoutConstraintAxisHorizontal];
[self.buttonsStackView addArrangedSubview:spacerButton];
And Make
self.buttonsStackView.distribution = UIStackViewDistributionFill;

Swift 4.2 + iOS 12.1 tested:
let stackView: UIStackView = UIStackView(frame: .zero)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.alignment = .leading // <- aligns each of your items to the left.
// further setup + setup constraints
// ...

Thanks to #Ankit Maurya, solution with example for swift
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.distribution = .fill
let spacer = UIView()
spacer.isUserInteractionEnabled = false
spacer.setContentHuggingPriority(.fittingSizeLevel, for: .horizontal)
spacer.setContentCompressionResistancePriority(.fittingSizeLevel, for: .horizontal)
// stackView.addArrangedSubview.... any u need
stackView.addArrangedSubview(spacer)
Example:

Use the UIStackView.alignment property.
Reference: https://developer.apple.com/reference/uikit/uistackview/1616243-alignment

Thanks for the advice guys. I tried playing with the options on UIStackView.alignment but it never really worked as desired. In the end I messed with the width of the UIStackView to achieve the desired results.
if(prdDTO.getShellIcn() == "Y")
{
let icon = UIImageView()
icon.contentMode = UIViewContentMode.scaleAspectFit
icon.image = #imageLiteral(resourceName: "Shellfish")
prdIngredients.addArrangedSubview(icon)
stackviewWidth += 25
}
if(prdDTO.getVegeIcn() == "Y")
{
let icon = UIImageView()
icon.contentMode = UIViewContentMode.scaleAspectFit
icon.image = #imageLiteral(resourceName: "Vege")
prdIngredients.addArrangedSubview(icon)
stackviewWidth += 25
}
if(prdDTO.getSpicyIcn() == "Y")
{
let icon = UIImageView()
icon.contentMode = UIViewContentMode.scaleAspectFit
icon.image = #imageLiteral(resourceName: "Spicy")
prdIngredients.addArrangedSubview(icon)
stackviewWidth += 25
}
prdIngredients.widthAnchor.constraint(equalToConstant: CGFloat(stackviewWidth))

Remove the trailing constrant (even though XCode complains!!!) and your problem will be solved. This will cause the the view to expand as you add items to it.

Storyboard
Click Attributes Inspector
Select Trailing from the Drop Down menu when Alignment is selected.
Programmatically
Change the UIStackView.alignment property when initializing.

Related

Horizontal UIStackView - fix center item's size and stretch other items

I am trying to achieve a layout like this:
Essentially, I need the or label to stay in the middle and occupy a fixed width and the lines to stretch out towards the edges of the screen.
Here's what I did in the XIB:
created a horizontal UIStackview, with center alignment.
Set the height constraint of the stackview to 20, distribution to fill.
added two UIView elements(for the gray lines), with a height constraint set to 5.
Added a UILabel between the two UIView elements above.
Added more constraints:
left UIView leads with 0 from superview and trails with 5 to middle label
right UIView leads with 4 from middle label and trails with 0 to superview.
Looks fine on the Interface builder, but on different screen sizes and landscapes, I find the middle "or" label to stretch and kick away the left and right UIViews to make up for the available space, which seems right:
If I set a width constraint of 20 on the middle label, the right UIView stretches unevenly like this:
I know the CHP of the elements matters to some extent here, I have even tried setting CHPs like this:
CHP of the middle label is 251
CHP of left and right UIViews is 250.
This still leaves me with the uneven stretching of right UIView.
What is it that I am doing wrong? Insights much appreciated!
Thanks a lot!
You need to set widthConstraint on UIStackView and the make leftView.widthAnchor = rightView.widthAnchor. Alternatively you can set leading and trailing constraints on UIStackView and then set leftView.widthAnchor = rightView.widthAnchor.
Below is the sample code you can try out in Playgrounds
let leftView = UIView()
leftView.translatesAutoresizingMaskIntoConstraints = false
leftView.backgroundColor = .lightGray
let rightView = UIView()
rightView.translatesAutoresizingMaskIntoConstraints = false
rightView.backgroundColor = .lightGray
let orLabel = UILabel()
orLabel.translatesAutoresizingMaskIntoConstraints = false
orLabel.textColor = .black
orLabel.text = "or"
let stackView = UIStackView(arrangedSubviews: [leftView, orLabel, rightView])
stackView.alignment = .center
stackView.distribution = .fill
stackView.axis = .horizontal
stackView.spacing = 5
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 20).isActive = true
stackView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
leftView.heightAnchor.constraint(equalToConstant: 5).isActive = true
rightView.heightAnchor.constraint(equalToConstant: 5).isActive = true
leftView.widthAnchor.constraint(equalTo: rightView.widthAnchor).isActive = true
Don't set any leading or trailing constraints...
Set "right view" width constraint equal to "left view" width, and give your stack view a spacing value of 4 or 5.
Storyboard:
Portrait:
Landscape:

Programmatically changing size of 1 subview in UIStackView

I am currently making a calculator and want to change the size of my 0 button to the size of 2 subviews - which is half the size of the entire view. I want it to look exactly like apples calculator app, where the 0 is bigger than all the other buttons.
The way i layout my view is by having a vertical UIStackView and adding horizontal UIStackView's to it, just like the picture below.
Therefore, i want the last horizontal stack to have 3 arranged subviews but make the 0 button fill the exceeding space, so the , and = buttons are the same size as all other buttons.
Thank you.
Programmatically, you could set multiplier with constraint(equalTo:multiplier:), official docs: https://developer.apple.com/documentation/uikit/nslayoutdimension/1500951-constraint
So we could constraint the last two button with same width and make the first one two times longer than one of the other two.
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let btn1 = UIButton()
let btn2 = UIButton()
let btn3 = UIButton()
btn1.backgroundColor = .red
btn2.backgroundColor = .yellow
btn3.backgroundColor = .blue
let hStack = UIStackView(arrangedSubviews: [btn1, btn2, btn3])
hStack.axis = .horizontal
hStack.spacing = 1
view.addSubview(hStack)
hStack.translatesAutoresizingMaskIntoConstraints = false
hStack.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
hStack.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
hStack.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
// Here would be what you need:
btn2.widthAnchor.constraint(equalTo: btn3.widthAnchor).isActive = true
btn1.widthAnchor.constraint(equalTo: btn2.widthAnchor, multiplier: 2).isActive = true
}
You can use stackview.distribution = .fillEquallly for first 4 horizontal stackviews and use stackview.distribution = .fillPropotionally for the last horizontal stackview.
Then set with constraint for the 0 button to 50% of last horizontal stackview's width.

Stackview inside Other stackview width issue

I have an Stackview created in IB and it has Vertical orientation. This stackview has equal width to parent view.
Now I am created a Stackview programmatically for example
let stackViewHorizontal = UIStackView()
stackViewHorizontal.axis = UILayoutConstraintAxis.horizontal
stackViewHorizontal.distribution = UIStackViewDistribution.fillEqually
stackViewHorizontal.alignment = UIStackViewAlignment.leading
stackViewHorizontal.spacing = 8
stackViewHorizontal.translatesAutoresizingMaskIntoConstraints = false
stackViewHorizontal.leadingAnchor.constraint(equalTo: mainStackView.leadingAnchor,constant:0)
stackViewHorizontal.trailingAnchor.constraint(equalTo: mainStackView.trailingAnchor,constant:0)
Here mainStackView is a stackview which is created Via IB.and stackviewHorizontal is a stackview that is created programatically.
I am putting to UILabels inside stackViewHorizontal. I expected this will expand to full length and each UiLabel will take 50% of the screen in width since stackview has horizontal axis and distribution is fillEqually.
But I am having a UiLabels next to eachother horizontally. but not taking full width of screen
What I am doing wrong please notify?
Activate constraints, also give it a height:
stackViewHorizontal.leadingAnchor.constraint(equalTo: mainStackView.leadingAnchor,constant:0).isActive = true
stackViewHorizontal.trailingAnchor.constraint(equalTo: mainStackView.trailingAnchor,constant:0).isActive = true
OR
NSLayoutConstraint.activate([
stackViewHorizontal.leadingAnchor.constraint(equalTo: mainStackView.leadingAnchor,constant:0),
stackViewHorizontal.trailingAnchor.constraint(equalTo: mainStackView.trailingAnchor,constant:0)
])
//
let stackViewHorizontal = UIStackView()
stackViewHorizontal.axis = UILayoutConstraintAxis.horizontal
stackViewHorizontal.distribution = UIStackViewDistribution.fillEqually
stackViewHorizontal.alignment = UIStackViewAlignment.leading
stackViewHorizontal.spacing = 8
self.view.addSubview(stackViewHorizontal) //// add it here
stackViewHorizontal.translatesAutoresizingMaskIntoConstraints = false
stackViewHorizontal.leadingAnchor.constraint(equalTo: mainStackView.leadingAnchor, constant: 0).isActive = true
stackViewHorizontal.trailingAnchor.constraint(equalTo: mainStackView.trailingAnchor, constant: 0).isActive = true

Centering an UIImage (horizontally or vertically) with programatic constraints UIKit

I am trying to simply center a 5x5 image in a horizontal row in UIKit.
On the left of the view I have a UIView that has its left and top margins attached to the parent view and has intrinsic width and height. It might not exactly fill half of the containing view (so assume its width is arbitrary).
To the right of it (but on the same row) I am trying to put a UIImage that is centered in the blank space to the right of the left object. Meaning it is both centered horizontally and vertically but to the right of the other object.
I really have no clue how to accomplish this although I figure there are both more and less elegant ways to achieve this.
After you set a proper width and height for it set these constraints
smallView.centerYAnchor.constraint(equalTo: greenView.centerYAnchor).isActive = true
smallView.centerXAnchor.constraint(equalTo: parentView.centerXAnchor, multiplier:1.5).isActive = true
It's pretty simple. You'll use a layout guide to define the beginning and end of the horizontal space. Then you'll center your view in the layout guides center. I have included layoutMarginsGuide which can be adjusted by setting container.layoutMargins.
let containerView = UIView()
containerView.backgroundColor = .gray
// Add view to parent ...
let greenView = UIView()
greenView.translatesAutoresizingMaskIntoConstraints = false
greenView.backgroundColor = .green
containerView.addSubview(greenView)
greenView.topAnchor.constraint(equalTo: containerView.layoutMarginsGuide.topAnchor).isActive = true
greenView.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
let horizontalGuide = UILayoutGuide()
containerView.addLayoutGuide(horizontalGuide)
horizontalGuide.leadingAnchor.constraint(equalTo: greenView.trailingAnchor, constant: containerView.layoutMargins.left).isActive = true
horizontalGuide.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.backgroundColor = .red
containerView.addSubview(imageView)
imageView.centerXAnchor.constraint(equalTo: horizontalGuide.centerXAnchor).isActive = true
imageView.centerYAnchor.constraint(equalTo: greenView.centerYAnchor).isActive = true

Why is image view stretching to fill half the width in a UIStackView?

I am taking an UIImageView on the left of the horizontal stack view and a label with number of lines set to 0. The issue is that both the UIImageView and the label are filling equal space even though I have set the stack view distribution property to fill. I don't want my image view to expand. Changing content hugging isn't helping.
Problem is: When telling the stack view to fill the content area it try to do it as best as possible. When setting the distribution type to fill it do not know how to fill. You have to provide this information by setting a width constraint via autolayout for the image. The label will fill the remaining area then.
Another way of doing this would be to add an invisible spacer object at the right side of the image + label stack.
private func spacer() -> UIView {
let stretchingView = UIView()
stretchingView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
stretchingView.backgroundColor = UIColor.clear
stretchingView.translatesAutoresizingMaskIntoConstraints = false
return stretchingView
}
private func labelWithImage() -> UIView {
let image = UIImage(systemName: "map")
let imageView = UIImageView(image: image)
let label = UILabel()
label.text = "3 Miles Away"
label.font = UIFont.systemFont(ofSize: 17, weight: .regular)
label.textColor = UIColor.gray
let stackView = UIStackView(arrangedSubviews: [imageView, label, spacer()])
stackView.alignment = .leading
stackView.axis = .horizontal
return stackView
}

Resources