Swift, constraint, UILabel, anchor right - ios

Desired...
For some reason the following doesn't work:
extension UIView {
func addWordOnRight() {
let l = UILabel()
//l.frame = self.bounds ..?
l.textAlignment = .right
l.text = "blah"
self.addSubview(l)
let m = self.layoutMarginsGuide
l.trailingAnchor.constraint(equalTo: m.trailingAnchor, constant: 0).isActive = true
l.centerYAnchor.constraint(equalTo: m.centerYAnchor, constant: 0).isActive = true
}
}
And if you l.frame = self.bounds, it mysteriously anchors to the left, not right.

By default the label has translatesAutoresizingMaskIntoConstraints set to true, so the view automatically adds constraints. When you add your own constraints these conflict with those already added. (You should be seeing lots of warnings in your console.)
If you want to use autolayout set l.translatesAutoresizingMaskIntoConstraints = false. Your constraints should now work correctly.
In case you also want to set the width and height of the label, set those using constraints as well (leave the frame alone).

Related

Why I cannot modify a UIScrollview leading edge programmatically?

I'm fairly new to iOS app development. I'm trying to make an iOS app which will have an horizontal scroll view with labels added as subviews which exhibit a snapping behaviour. I'm using UIKit and auto layouts (Swift 5, Xcode 12.01) for a programmatically build UI. I have three labels to be added to the scroll view, one is centred, one is offset 500 pt from the scrollview centre (right) and one is at -500 from the centre (left)
When I try to set the scrollview trailing anchor referring to the label on the right I have no problems. However, when I try to set the leading edge referring to the label on the left (leading edge), I encounter the error: [LayoutConstraints] Unable to simultaneously satisfy constraints.
Am I missing something?
I attach my code below:
// Create scroll view on top of bottom view
func createScrollView() -> UIScrollView {
let myScrollView = UIScrollView()
myScrollView.isScrollEnabled = true
myScrollView.backgroundColor = .red
myScrollView.showsHorizontalScrollIndicator = true
view.addSubview(myScrollView)
// Constraints
myScrollView.translatesAutoresizingMaskIntoConstraints = false
myScrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
myScrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
myScrollView.bottomAnchor.constraint(equalTo: bottomView.firstBaselineAnchor).isActive = true
myScrollView.heightAnchor.constraint(equalToConstant: 50).isActive = true
// Add some elements to the view
let label1 = UILabel()
let label2 = UILabel()
let label3 = UILabel()
label1.text = "EV 0."
label1.font = .systemFont(ofSize: 20)
label1.translatesAutoresizingMaskIntoConstraints = false
label2.text = "ISO"
label2.font = .systemFont(ofSize: 20)
label2.translatesAutoresizingMaskIntoConstraints = false
label3.text = "SNAP"
label3.font = .systemFont(ofSize: 20)
label3.translatesAutoresizingMaskIntoConstraints = false
// Add labels to scroll view
myScrollView.addSubview(label1)
myScrollView.addSubview(label2)
myScrollView.addSubview(label3)
// Add constraint
label1.centerYAnchor.constraint(equalTo: myScrollView.centerYAnchor).isActive = true
label2.centerYAnchor.constraint(equalTo: myScrollView.centerYAnchor).isActive = true
label3.centerYAnchor.constraint(equalTo: myScrollView.centerYAnchor).isActive = true
label1.centerXAnchor.constraint(equalTo: myScrollView.centerXAnchor, constant: -300).isActive = true
label2.centerXAnchor.constraint(equalTo: myScrollView.centerXAnchor, constant: 300).isActive = true
label3.centerXAnchor.constraint(equalTo: myScrollView.centerXAnchor, constant: 0).isActive = true
// Add last constraint in the scroll view
myScrollView.leadingAnchor.constraint(equalTo: label1.leadingAnchor, constant: -500).isActive = true
myScrollView.trailingAnchor.constraint(equalTo: label2.trailingAnchor, constant: 500).isActive = true
return myScrollView
Thanks a lot for your help!!!

UIScrollView doesn't respond to gestures

I have a UIScrollView which is a dashboard basically with relevant information for the user. It has a pager underneath and contains three UIViews at the moment.
I used to set the different views with a CGRect as the frame, setting the x offset to the width of the UIScrollView * the page number. This worked fine, but it was a hassle to make the UIScrollView disappear when scrolling in the UITableView underneath.
I am now using AutoLayout with constraints to display the information, which works fine. However, my UIScrollView is not scrolling anymore. It does not respond to any swipe gestures I perform. However, it does respond to taps in the pager, which shows me the offsets and constraints are right, I am just unable to scroll in it. Take a look:
My UIScrollView is made up like this:
var dashboardView: UIScrollView = {
let dashboardView = UIScrollView()
dashboardView.translatesAutoresizingMaskIntoConstraints = false
dashboardView.backgroundColor = UIColor.clear
dashboardView.layer.masksToBounds = true
dashboardView.layer.cornerRadius = 10
dashboardView.isPagingEnabled = true
dashboardView.showsHorizontalScrollIndicator = false
dashboardView.showsVerticalScrollIndicator = false
dashboardView.alwaysBounceHorizontal = false
dashboardView.alwaysBounceVertical = false
return dashboardView
}()
I then set the different views like so:
for index in 0..<3 {
let currentDash = UIView()
currentDash.backgroundColor = UIColor.lightGray
currentDash.layer.cornerRadius = 10
currentDash.layer.masksToBounds = false
currentDash.translatesAutoresizingMaskIntoConstraints = false
currentDash.isUserInteractionEnabled = false
dashboardView.addSubview(currentDash)
currentDash.topAnchor.constraint(equalTo: dashboardView.topAnchor).isActive = true
currentDash.bottomAnchor.constraint(equalTo: dashboardView.bottomAnchor).isActive = true
currentDash.leadingAnchor.constraint(equalTo: dashboardView.leadingAnchor, constant: dashboardWidth * CGFloat(index)).isActive = true
currentDash.trailingAnchor.constraint(equalTo: dashboardView.leadingAnchor, constant: (dashboardWidth * CGFloat(index)) + dashboardWidth).isActive = true
currentDash.widthAnchor.constraint(equalToConstant: dashboardWidth).isActive = true
currentDash.heightAnchor.constraint(equalToConstant: dashboardHeight).isActive = true
}
When I remove the constraints (AutoLayout) and set it with frames it works perfectly, like this:
dashFrame.origin.x = dashboardWidth * CGFloat(index)
dashFrame.size = CGSize(width: dashboardWidth, height: dashboardHeight)
let currentDash = UIView(frame: dashFrame)
But then, because of setting the frame, the view doesn't scroll up as I would, which is why I want to use AutoLayout.
Any ideas or suggestions as to what I am doing wrong? My ScrollViewDidScroll() method simply gets not called, because a simple print doesn't return anything in my console.
I have tried setting the isUserInteractionEnabled property to false on the currentDash UIView, which was no success. I have also tried removing all constraints separately, some together, etc. - but also this doesn't work - I need a topAnchor, bottomAnchor, heightAnchor and widthAnchor at least.
Oh, and, of course, I set the contentSize property:
dashboardView.contentSize = CGSize(width: dashboardWidth * CGFloat(dashboardPager.numberOfPages), height: dashboardHeight)
Any suggestions or hints towards the right answer would be deeply appreciated. Thanks!
EDIT: I also set constraints for the UIScrollView itself:
dashboardView.topAnchor.constraint(equalTo: dashboardBG.topAnchor).isActive = true
dashboardView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: normalSpacing).isActive = true
dashboardView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -normalSpacing).isActive = true
dashboardView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
dashboardView.heightAnchor.constraint(equalToConstant: dashboardHeight).isActive = true
dashboardView.delegate = self
The issue is here
currentDash.leadingAnchor.constraint(equalTo: dashboardView.leadingAnchor, constant: dashboardWidth * CGFloat(index)).isActive = true
currentDash.trailingAnchor.constraint(equalTo: dashboardView.leadingAnchor, constant: (dashboardWidth * CGFloat(index)) + dashboardWidth).isActive = true
1- Regarding leading of the views , the leading of the first view should be pinned to the scrollView , the leading of the second view pinned to the trailing of the previous and so on , so
if index == 0{
// make leading with scrolview leading
else
{
// make leading with prevView trailing
}
So make a var initiated with scrollView and change end of for loop
2- Regarding the trailing , only the last view trailing be pinned to the trailing of the scrollview
if index == 2 { // last
// make trailing with scrollView
}
I eventually figured it out thanks to Sh_Khan’s answer. I held on to the original code, but removed the trailingAnchor for all indexes and added it only to the last index.
for index in 0..<3 {
let currentDash = UIView()
currentDash.backgroundColor = UIColor.lightGray
currentDash.layer.cornerRadius = 10
currentDash.layer.masksToBounds = false
currentDash.translatesAutoresizingMaskIntoConstraints = false
currentDash.isUserInteractionEnabled = false
dashboardView.addSubview(currentDash)
currentDash.topAnchor.constraint(equalTo: dashboardView.topAnchor).isActive = true
currentDash.bottomAnchor.constraint(equalTo: dashboardView.bottomAnchor).isActive = true
currentDash.leadingAnchor.constraint(equalTo: dashboardView.leadingAnchor, constant: dashboardWidth * CGFloat(index)).isActive = true
currentDash.widthAnchor.constraint(equalToConstant: dashboardWidth).isActive = true
currentDash.heightAnchor.constraint(equalToConstant: dashboardHeight).isActive = true
if index == 2 {
currentDash.trailingAnchor.constraint(equalTo: dashboardView.trailingAnchor).isActive = true
}
}
Thanks for the help!

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

Set X and Y values of UILabel using NSLayout

I am adding a UI Label and I am trying out programmatic UI code. I set up my label using this code:
let titleLabel: UILabel = {
let lb = UILabel()
lb.translatesAutoresizingMaskIntoConstraints = false
lb.textAlignment = .center
lb.numberOfLines = 1
lb.textColor = UIColor.black
lb.font=UIFont.systemFont(ofSize: 22)
lb.backgroundColor = UIColor.white
return lb
and then added constraints using this code:
func setupTitleLabel() {
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.heightAnchor.constraint(equalToConstant: 100).isActive = true
titleLabel.widthAnchor.constraint(equalToConstant: 200).isActive = true
titleLabel.centerXAnchor.constraint(equalTo: titleLabel.superview!.centerXAnchor).isActive = true
titleLabel.centerYAnchor.constraint(equalTo: titleLabel.superview!.centerYAnchor).isActive = true
}
I call this function later on. However, I am not sure how to change to X and Y values. Currently, it is set to the middle of the page for both X and Y, but how would I move it up to the top.
As I said in my comment, you are using titleLabel.superview!.centerYAnchor wrongly if you need pin your label to top of your label superview you need use titleLabel.superview!.topAnchor instead
replace this
titleLabel.centerYAnchor.constraint(equalTo: titleLabel.superview!.centerYAnchor).isActive = true
by this
titleLabel.centerYAnchor.constraint(equalTo: titleLabel.superview!.topAnchor).isActive = true
What I understand the case is that the label is in the centerX and centerY, and you want it to be at the top of the superView later.If so,I think you can use var centerYAnchor: NSLayoutConstraint?
centerYAnchor = titleLabel.centerYAnchor.constraint(equalTo: titleLabel.superview!.centerYAnchor)
centerYAnchor.isActive = true
And when you want to change the label at the top later,you can set centerYAnchor.isActive = false and set the label's topAnchor.
titleLabel.topAnchor.constraint(equalTo: titleLabel.superview!.topAnchor).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