Divide stackview in three subviews with a dashed divider line? - ios

I'm trying to divide a UIStackView intro three separate subviews and have them divided by a dashed line. I know you can set spacing on a UIStackView but as far as I'm aware you cannot change that spacing to be a dashed line.
Basically I want my three subviews to scale properly on different device sizes but the dashed line to always be small in between them. For clarity the result I'm trying to achieve looks like this:
I hope someone can point me in the right direction, thanks in advance!

You can constrain the 3 views to have equal width, and then add a couple of separator views constrained to a constant width.
let stackView = UIStackView()
stackView.axis = .horizontal
self.view.addSubview(stackView)
let view1 = UIView()
view1.backgroundColor = .red
stackView.addArrangedSubview(view1)
let separator1 = UIView()
separator1.backgroundColor = .black
stackView.addArrangedSubview(separator1)
separator1.widthAnchor.constraint(equalToConstant: 1).isActive = true
let view2 = UIView()
view2.backgroundColor = .green
stackView.addArrangedSubview(view2)
view2.widthAnchor.constraint(equalTo: view1.widthAnchor, multiplier: 1).isActive = true
let separator2 = UIView()
separator2.backgroundColor = .black
stackView.addArrangedSubview(separator2)
separator2.widthAnchor.constraint(equalToConstant: 1).isActive = true
let view3 = UIView()
view3.backgroundColor = .blue
stackView.addArrangedSubview(view3)
view3.widthAnchor.constraint(equalTo: view1.widthAnchor, multiplier: 1).isActive = true

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.

How to make a horizontal StackView to have the first element's width and fill the rest of it

I'm new with swift and trying to create an input field at the moment. My problem is, that I would like to have a Label as shown in the picture:
So far, I'm working with StackViews: One vertical one for the input fields, and three horizontal ones to have the Title and the user input. My code so far is as follows:
// Initialize outter stackview
let feedbackOutterSV = UIStackView()
view.addSubview(feedbackOutterSV)
feedbackOutterSV.translatesAutoresizingMaskIntoConstraints = false
feedbackOutterSV.axis = NSLayoutConstraint.Axis.vertical
NSLayoutConstraint.activate([
feedbackOutterSV.topAnchor.constraint(equalTo: tutorialText.bottomAnchor, constant: 10),
feedbackOutterSV.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
feedbackOutterSV.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
feedbackOutterSV.heightAnchor.constraint(equalToConstant: 300)
])
// Initalize inner stackview for title
let feedbackInnerSVTitle = UIStackView()
feedbackOutterSV.addArrangedSubview(feedbackInnerSVTitle)
feedbackInnerSVTitle.translatesAutoresizingMaskIntoConstraints = false
feedbackInnerSVTitle.axis = .horizontal
feedbackInnerSVTitle.alignment = .fill
feedbackInnerSVTitle.distribution = .fillProportionally
let titleLabel = UILabel()
feedbackInnerSVTitle.addArrangedSubview(titleLabel)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.text = "feedback.input.title".localize()
titleLabel.font = UIFont.preferredFont(forTextStyle: .body)
titleLabel.textColor = .gray
let titleTextView = UITextView()
feedbackInnerSVTitle.addArrangedSubview(titleTextView)
titleTextView.translatesAutoresizingMaskIntoConstraints = false
titleTextView.font = UIFont.preferredFont(forTextStyle: .body)
titleTextView.isScrollEnabled = false
NSLayoutConstraint.activate([
titleLabel.widthAnchor.constraint(equalToConstant: 39)
])
This code gives the expected output for English, however I have to implement it in different languages, so I can't use a constant width.
Can anyone tell me how to change my code, so I don't need the constant constraint but the width of the Label is adjusted to the length of the word?
Thanks in advance
Couple things...
I assume you want the "title label" to be top-aligned with your textView, so change .fill to .top:
feedbackInnerSVTitle.alignment = .top // .fill
and, don't use .fillProportionally
feedbackInnerSVTitle.distribution = .fill // .fillProportionally
Now, you'll likely see each element taking 50% of the width, so change the content hugging priority for your title label:
titleLabel.setContentHuggingPriority(.required, for: .horizontal)
and, finally, don't set a width constraint on your title label:
// NSLayoutConstraint.activate([
// titleLabel.widthAnchor.constraint(equalToConstant: 39)
// ])
Result:
In your code, width constraint on titleLabel must be set to titleLabel.intrinsicContentSize.width
NSLayoutConstraint.activate([
titleLabel.widthAnchor.constraint(equalToConstant: titleLabel.intrinsicContentSize.width)
])
Also, set the distribution of feedbackInnerSVTitle as .fill
feedbackInnerSVTitle.distribution = .fill
I think you could use NSLayoutConstraint.activate([
titleLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: 0)] to let it grow depending on the content

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

UIStackview with differently aligned subviews

I'd like to align the Blue and Purple views to the center of the screen, and I'd like to align the green view to the left of the screen:
Here is my code:
view.backgroundColor = UIColor.orangeColor()
//Stackview:
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(stackView)
stackView.topAnchor.constraintEqualToAnchor(self.view.topAnchor).active = true
stackView.leftAnchor.constraintEqualToAnchor(self.view.leftAnchor).active = true
stackView.rightAnchor.constraintEqualToAnchor(self.view.rightAnchor).active = true
stackView.axis = UILayoutConstraintAxis.Vertical
stackView.alignment = .Center
//Blue view:
let blueBox = UIView()
stackView.addArrangedSubview(blueBox)
blueBox.backgroundColor = UIColor.blueColor()
blueBox.heightAnchor.constraintEqualToConstant(140).active = true
blueBox.widthAnchor.constraintEqualToConstant(140).active = true
//Inner stackview that contains the green view:
let greenBoxContainer = UIStackView()
let greenBox = UIView()
stackView.addArrangedSubview(greenBoxContainer)
greenBoxContainer.addArrangedSubview(greenBox)
greenBoxContainer.alignment = .Leading
//Green view:
greenBox.backgroundColor = UIColor.greenColor()
greenBox.widthAnchor.constraintEqualToConstant(120).active = true
greenBox.heightAnchor.constraintEqualToConstant(120).active = true
//Purple view:
let purpleView = UIView()
stackView.addArrangedSubview(purpleView)
purpleView.backgroundColor = UIColor.purpleColor()
purpleView.heightAnchor.constraintEqualToConstant(50.0).active = true
purpleView.widthAnchor.constraintEqualToConstant(50.0).active = true
To repeat, how can I align the left edge of the green view with the left edge of the screen?
I tried this:
greenBoxContainer.widthAnchor.constraintEqualToAnchor(stackView.widthAnchor).active = true
but it only stretches the green view through the length of the screen.
One way is to add a spacer view:
let greenBox = UIView()
stackView.addArrangedSubview(greenBoxContainer)
greenBoxContainer.addArrangedSubview(greenBox)
let spacer = UIView()
greenBoxContainer.addArrangedSubview(spacer)
spacer.rightAnchor.constraintEqualToAnchor(greenBoxContainer.rightAnchor).active = true
greenBoxContainer.widthAnchor.constraintEqualToAnchor(stackView.widthAnchor).active = true
greenBox.backgroundColor = UIColor.greenColor()
greenBox.widthAnchor.constraintEqualToConstant(120).active = true
greenBox.heightAnchor.constraintEqualToConstant(120).active = true
Note that I'm no longer setting greenBoxContainer's alignment, so it defaults to .Fill. Thus the right edge of spacer is flushed to the inner stackview's right edge, and it takes up all the width, leaving greenBox just enough room to satisfy its width constraint.
But this is a workaround, I'd like to be able to specify alignments without having to create spacer views..

Resources