Swift UIStackView: Positioning elements from center programmatically - swift

I am trying to get elements inside a UIStackView to position themselves equally from the center.
This is the desired effect I would like:
As you can see, I would like the two text fields to be equally spaced from each other and aligned center within the stack view.
This stack view will have either 1 - 7 textfields that I need lined up.
Here is what is currently coming out:
This is how I am setting the Text Fields
let textLabel = UILabel()
textLabel.backgroundColor = UIColor.red
textLabel.widthAnchor.constraint(equalToConstant: 40.0).isActive = true
textLabel.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
textLabel.text = "Hi"
textLabel.textAlignment = .center
let textLabel1 = UILabel()
textLabel1.backgroundColor = UIColor.red
textLabel1.widthAnchor.constraint(equalToConstant: 40.0).isActive = true
textLabel1.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
textLabel1.text = "Hi"
textLabel1.textAlignment = .center
I am generating the stack view like so:
//Stack View
let stackView = UIStackView()
stackView.axis = UILayoutConstraintAxis.horizontal
stackView.distribution = UIStackViewDistribution.equalCentering
stackView.alignment = UIStackViewAlignment.fill
stackView.spacing = 16.0
stackView.addArrangedSubview(textLabel)
stackView.addArrangedSubview(textLabel1)
stackView.translatesAutoresizingMaskIntoConstraints = false;
self.view.addSubview(stackView)
//Constraints
stackView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 50).isActive = true
stackView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -50).isActive = true
stackView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100).isActive = true
How do I get the elements inside to align center?
I thought it was
UIStackViewDistribution.equalCentering
However, that didn't do much.

Thats odd. Normally a horizontal UIStackView with center alignment and equal centering distribution should do exactly that:
But maybe I'm not understanding you correct and you actually want something like this:
In which case you have to chose Fill Equally as the distribution method and make the labels itself center their text:

Related

How to make vertical labels bar in Swift?

I would like to make such layout via swift:
I tried to make it in such way:
var buttonArray = [UILabel]()
for (myKey,myValue) in colorDictionary{
buttonArray += [colorButton(withColor: myValue, title: myKey)]
}
let horizontalStack = UIStackView(arrangedSubviews: buttonArray)
horizontalStack.axis = .horizontal
horizontalStack.distribution = .fillEqually
horizontalStack.alignment = .fill
horizontalStack.translatesAutoresizingMaskIntoConstraints = false
horizontalStack.transform = CGAffineTransform(rotationAngle: -CGFloat.pi / 2)
let label2 = UILabel()
label2.text = "Label"
label2.backgroundColor = .red
label2.textColor = .white
label2.textAlignment = .center
label2.lineBreakMode = .byCharWrapping
label2.numberOfLines = 0
label2.translatesAutoresizingMaskIntoConstraints = false
label2.transform = CGAffineTransform(rotationAngle: -CGFloat.pi / 2)
let mainStackView = UIStackView()
mainStackView.axis = .horizontal
mainStackView.translatesAutoresizingMaskIntoConstraints = false
mainStackView.addArrangedSubview(label2)
mainStackView.addArrangedSubview(horizontalStack)
mainContainer.addSubview(mainStackView)
NSLayoutConstraint.activate([
mainStackView.topAnchor.constraint(equalTo: mainContainer.topAnchor, constant: 5),
mainStackView.leftAnchor.constraint(equalTo: mainContainer.leftAnchor,
constant: 20),
mainStackView.rightAnchor.constraint(equalTo: mainContainer.rightAnchor,
constant: -20),
mainStackView.heightAnchor.constraint(equalToConstant: 270),
])
where colorButton is:
func colorButton(withColor color:UIColor, title:String) -> UILabel{
let newButton = UILabel()
newButton.backgroundColor = color
newButton.text = title
newButton.textAlignment = .center
newButton.textColor = UIColor.white
return newButton
}
and here is the result which I got:
How I can make all these labels look like the desired image? And also I'm not sure whether label rotation can be done by my method.
What I would do is first get it working without any transformations so it appears as a normal, not rotated setup. Once that is working, you only need to apply a single transformation to the main stack view to get the whole thing rotated. Then finally you can tweak the constraints to get it positioned correctly.
Here is code that works:
let horizontalStack = UIStackView(arrangedSubviews: buttonArray)
horizontalStack.axis = .horizontal
horizontalStack.distribution = .fillEqually
horizontalStack.alignment = .fill
horizontalStack.translatesAutoresizingMaskIntoConstraints = false
let label2 = UILabel()
label2.text = "Label"
label2.backgroundColor = .red
label2.textColor = .white
label2.textAlignment = .center
label2.lineBreakMode = .byCharWrapping
label2.numberOfLines = 0
label2.translatesAutoresizingMaskIntoConstraints = false
let mainStackView = UIStackView()
mainStackView.axis = .vertical
mainStackView.distribution = .equalSpacing
mainStackView.translatesAutoresizingMaskIntoConstraints = false
mainStackView.addArrangedSubview(label2)
mainStackView.addArrangedSubview(horizontalStack)
mainStackView.transform = CGAffineTransform(rotationAngle: -CGFloat.pi / 2)
mainContainer.addSubview(mainStackView)
NSLayoutConstraint.activate([
mainStackView.centerYAnchor.constraint(equalTo: mainContainer.centerYAnchor),
mainStackView.centerXAnchor.constraint(equalTo: mainContainer.leftAnchor, constant: 40),
// Set the "width", not the "height"
mainStackView.widthAnchor.constraint(equalToConstant: 270),
])
Changes:
I removed the transformation you had for the horizontal stack view and the big label.
I added a single transformation to the main stack view.
I used a vertical, not horizontal, stack view for the main stack.
Updated the properties of the main stack view so the main label fills the area above the other labels.
Updated the constraints. Adjust those to suit your needs.
Note that you need to set the width of the main stack view since the constraint is relative to the untransformed main stack view.

constraint object by percentage in a stackview

I want to create a constraint that is 80 percent of the width of the uiview controller not a 100 percent which is what it is now. I tried using the var percent but its not working. The code should be centered on the y axis. So 10 percent on the left side and ride side are not covered by the constraint. I am trying to do this in a stack view.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//Image View
let percent = ((UIScreen.main.bounds.midY) + (UIScreen.main.bounds.maxX * 0.8))
//Text Label
let textLabel = UILabel()
textLabel.backgroundColor = UIColor.yellow
textLabel.widthAnchor.constraint(equalToConstant: self.view.frame.width * percent).isActive = true
textLabel.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
textLabel.text = "Hi World"
textLabel.textAlignment = .center
//Stack View
let stackView = UIStackView()
stackView.axis = NSLayoutConstraint.Axis.vertical
stackView.distribution = UIStackView.Distribution.equalSpacing
stackView.alignment = UIStackView.Alignment.center
stackView.spacing = 16.0
stackView.addArrangedSubview(textLabel)
stackView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(stackView)
//Constraints
stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
}
}
Set the widthAnchor of stackView with that of superView's width (in this case view controller's view) and set multiplier value as 0.80
stackView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.80).isActive = true
You can achieve this by setting 'widthAnchor' and 'centerXAnchor' like below. We need to always keep horizontal center of child view and super view same. Whereas width of child should be 80 percentage(here 0.8 indicates 80%) of super view.
stackView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.8, constant: 0).isActive = true
stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true

Constraint to set max height for items inside stackView

I have a stackView with UiViews boxes inside as you can see in the picture. I would like to set a max height of 50 or less for the boxes inside as they look too big right now but i also would like to keep the 1:1 ratio so that they would be square. I've tried to set a height constraint to the stackView but when i do that, the aspect ratio of 1:1 is being ignored.
Here's what it looks like right now:
And here's the code:
let stackview = UIStackView(arrangedSubviews: letterBoxes)
stackview.axis = .horizontal
stackview.spacing = 5
stackview.distribution = .fillEqually
stackview.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(stackview)
NSLayoutConstraint.activate([
stackview.topAnchor.constraint(equalTo: secondLine.bottomAnchor, constant: c/2 +
20),
stackview.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
stackview.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 10)
])
for box in letterBoxes{
box.aspectRation(1.0/1.0).isActive = true
}
Edit: i want the box to auto-resize and not overflow like the following (each time there's a different amount of boxes. so for example when there are only 3 boxes i want that the max size will be 50 but when there are 6 or higher number I want it to auto-resize so that it will fit the screen and not overflow. once I take the leading constraint off it overflows) :
The following will do the job.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let letterBoxes = [UIView(), UIView(), UIView()]
for box in letterBoxes {
box.translatesAutoresizingMaskIntoConstraints = false
box.backgroundColor = .systemBlue
view.addSubview(box)
let heightConstraint = box.heightAnchor
.constraint(equalToConstant: 50)
heightConstraint.priority = .defaultHigh
NSLayoutConstraint.activate([
box.widthAnchor.constraint(equalTo: box.heightAnchor),
heightConstraint
])
}
let stackview = UIStackView(arrangedSubviews: letterBoxes)
stackview.axis = .horizontal
stackview.spacing = 5
stackview.distribution = .fillEqually
stackview.alignment = .center
stackview.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackview)
NSLayoutConstraint.activate([
stackview.centerYAnchor.constraint(equalTo: view.centerYAnchor),
stackview.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stackview.leadingAnchor.constraint(
greaterThanOrEqualTo: view.leadingAnchor,
constant: 10
),
stackview.trailingAnchor.constraint(
lessThanOrEqualTo: view.trailingAnchor,
constant: -10
)
])
}
}
The point is to make the priorities of the heighConstraints for the boxes to be less than required.
So when there are not a lot of boxes, they will be of size 50, but when there are many, the height constraints will be ignored.
Also note that the stackview's leadingAnchor and the trailingAnchor constraint keeps the boxes in the screen bounds.
The following screenshots show the result:

Center UIStackView not centering view

I have this
view.addSubview(myName)
myName.topAnchor.constraint(equalTo: myImage.bottomAnchor).isActive = true
myName.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
myName.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
let stackView = UIStackView(arrangedSubviews: [myValue1,myValue2])
view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.alignment = .center
stackView.topAnchor.constraint(equalTo: myName.bottomAnchor).isActive = true
Any idea why it looks like this? I want it to be centered vertically like Example Text, I've tried adding
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
but then it looks like this
Any idea what I'm missing?
Try
stackView.centerXAnchor.constraint(equalTo: myName.centerXAnchor).isActive = true
This should center the stack view itself against the myName label
note that if you want stuff in stack view to align nicely you have to give it some proportional or fixed widths, and give your stackview a width.
Update
if you want space between labels, add an extra UIView() between them into the stack:
let spacer = UIView()
spacer.widthAnchor.constraint(equalToConstant: 40.0).isActive = true
let stackView = UIStackView(arrangedSubviews: [myValue1,spacer, myValue2])

UIStackView - why is the stack positioned one on top of the other?

I am trying to get a group of buttons arranged within a stack view. However, when i use a for-each loop to do this, the buttons end up being positioned one on top of the other?
var stackView: UIStackView = {
let sView = UIStackView()
sView.axis = .vertical
sView.distribution = .fill
sView.alignment = .center
return sView
}()
var optionTitles = ["Sign in", "sign out"]
for title in optionTitles{
let btn = UIButton()
btn.setTitle(title, for: .normal)
btn.backgroundColor = optionColour.withAlphaComponent(0.6)
allButtons.append(btn)
stackView.addSubview(btn)
}
// Auto layout constraints
for button in allButtons{
button.translatesAutoresizingMaskIntoConstraints = false
button.heightAnchor.constraint(equalToConstant: optionHeight).isActive = true
button.widthAnchor.constraint(equalTo: stackView.widthAnchor).isActive = true
}
view.addSubview(stackView)
stackTopConstraint = stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor)
stackBottomConstraint = stackView.bottomAnchor.constraint(equalTo: view.topAnchor)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
stackTopConstraint!.isActive = true
stackView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
2 things
1- You need to remove this
button.widthAnchor.constraint(equalTo: stackView.widthAnchor).isActive = true
2- Replace
stackView.addSubview(btn)
with
stackView.addArrangedSubview(btn)
You have:
stackTopConstraint = stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor)
stackBottomConstraint = stackView.bottomAnchor.constraint(equalTo: view.topAnchor)
You probably meant view.bottomAnchor in the second line. Surely you don't want to constraint the bottom of the stack view to the top of your view.