Multiple UIView with 0.5 height constraint not rendered the same way - ios

I have a vertical UIStackView and i populate it with some UIView's that has a fixed 0.5 height constraint set for them. This is how i create the lines:
for article in articles {
let border = UIView()
border.backgroundColor = UIColor(named: "color-hr")
border.translatesAutoresizingMaskIntoConstraints = false
articlesContainer.addArrangedSubview(border)
border.heightAnchor.constraint(equalToConstant: 0.5).isActive = true
}
However, once rendered on the phone, the lines are different in height. Any ideas what might cause this?

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.

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

Divide stackview in three subviews with a dashed divider line?

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

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

Resources