Programmatically changing size of 1 subview in UIStackView - ios

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.

Related

Can't get StackView to view items properly

I'm trying to add 4 items to a UIStackView, this 4 Items are all a simple square UIView, I added them all to a UIStackView but they won't stay square, it's like the UIStackView squeezes them or something. I tried setting the UIStackView to be the same height of the items, and set it's width to be the height of the items * 4 so I can try and get 1:1 ratio, but nothing worked for me.
The UIView is a simple UIView with background color. I tried to set it's widthAnchor and heightAnchor to 50, but I know the UIStackView has it's own way to size the items in it.
I don't really know what to do about this.
This is my UIStackView setup and constraints:
Setup:
private lazy var optionButtonStack: UIStackView = {
let stack = UIStackView(arrangedSubviews: [self.optionButton1, self.optionButton2, self.optionButton3, self.optionButton4])
stack.translatesAutoresizingMaskIntoConstraints = false
stack.distribution = .fillEqually
stack.axis = .horizontal
stack.spacing = 2.5
return stack
}()
Constraints:
private func setupOptionButtonStack() {
addSubview(optionButtonStack)
NSLayoutConstraint.activate([
optionButtonStack.heightAnchor.constraint(equalToConstant: 50),
optionButtonStack.widthAnchor.constraint(equalToConstant: 200),
optionButtonStack.centerXAnchor.constraint(equalTo: self.centerXAnchor),
optionButtonStack.bottomAnchor.constraint(equalTo: buyNowButton.topAnchor, constant: -8),
])
}
This is the UIView in case this is needed:
private let optionButton1: UIView = {
let view = UIView()
view.backgroundColor = .appBlue
view.translatesAutoresizingMaskIntoConstraints = false
view.widthAnchor.constraint(equalToConstant: 50).isActive = true
view.heightAnchor.constraint(equalToConstant: 50).isActive = true
view.tag = 1
return view
}()
Give the button view a single constraint setting its width equal to its height:
view.widthAnchor.constraint(equalTo: view.heightAnchor).isActive = true
and set the stack view alignment at center.

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:

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

Center label in UIView which has y offset of height of status bar

I am quite new to swift and trying to center a label and an image in a UIView which is located at the top of the screen. Currently the label is centered vertically and horizontally since this is the only thing I am able to do right now. As you can see I set autoresizing mask into constraints to false and used centerXAnchor and -YAnchor.
However I actually do not want the label to be in the center of the PostView but rather centered with a y offset of the height of the status bar. So it is centered but with no y offset of the height of the statusbar. Consequently, it looks kind of cramped(?): It is very close to the status bar... It looks like this:
But I would like to have the label (and later also an image) vertically centered in the red box:
This is the code I have right now (PostView class):
override init(frame: CGRect){
super.init(frame: frame)
//add subview containing name (and image)
infosContainerView.frame = frame
addSubview(infosContainerView)
//add sub view containing label to former UIView (infosContainerView)
infosContainerView.addSubview(infoNameView)
infoNameView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
infoNameView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
}
//this UIView shall contain the infoNameView and infoImageView
let infosContainerView: UIView = {
//set properties of controls container view
let entireInfoView = UIView()
entireInfoView.backgroundColor = .white
return entireInfoView
}()
//label and properties of label with name (autoresizingmaskinto constraint set to false)
let infoNameView: UILabel = {
//set properties of controls container view
let nameView = UILabel()
nameView.translatesAutoresizingMaskIntoConstraints = false
nameView.backgroundColor = .white
nameView.font = UIFont(name: "HelveticaNeue", size: 20)
nameView.text = "Name"
nameView.textColor = .black
nameView.textAlignment = .center
return nameView
}()
EDIT:
Jože Ws was close to solving the problem, instead of dividing by 2 one has to divide by 4 although I do not know why...:
let statusBarHeight = UIApplication.shared.statusBarFrame.height
infosContainerView.addSubview(infoNameView)
infoNameView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
infoNameView.centerYAnchor.constraint(equalTo: centerYAnchor, constant: statusBarHeight/4).isActive = true
Screenshot:
Replace centerYAnchor constraint init with
// infoNameView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
let statusBarHeight = UIApplication.shared.statusBarFrame.height
infoNameView.centerYAnchor.constraint(equalTo: centerYAnchor, constant: statusBarHeight/2).isActive = true
This will add an offset to centerYAnchor equal to the value of the statusBarHeight

UIStackview align left

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.

Resources