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!!!
Related
I'm trying to fix an issue with a UItextview which I placed at the bottom of a viewcontroller programmatically and sometimes it can clip through the bottom of the view if I don't set a constraint like so.
https://i.stack.imgur.com/YfyPi.png
Whenever I try to constraint the textview to the bottom of the safe area, the text needlessly expands too much if there's less text.
https://i.stack.imgur.com/8w1v2.png
Here's the relevant code snippets from the textview and the constraints respectively:
private let summaryTextView: UITextView = {
let summaryTextView = UITextView()
summaryTextView.textColor = .label
summaryTextView.backgroundColor = .customWhite
summaryTextView.textAlignment = .center
summaryTextView.font = UIFont.systemFont(ofSize: 24)
summaryTextView.clipsToBounds = true
summaryTextView.layer.cornerRadius = 20
summaryTextView.layer.masksToBounds = true
summaryTextView.isSelectable = false
summaryTextView.isEditable = false
summaryTextView.isScrollEnabled = false
summaryTextView.translatesAutoresizingMaskIntoConstraints = false
return summaryTextView
}()
private func setupConstraints() {
NSLayoutConstraint.activate([
imageContainerView.topAnchor.constraint(equalTo: view.topAnchor, constant: 120),
imageContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
imageContainerView.heightAnchor.constraint(equalToConstant: 280),
imageContainerView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.4),
summaryTextView.topAnchor.constraint(equalTo: imageContainerView.bottomAnchor,constant: 15),
summaryTextView.leadingAnchor.constraint(equalTo: view.leadingAnchor,constant: 10),
summaryTextView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -10),
summaryTextView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
backgroundImage.fillSuperView(to: view)
bookCover.fillSuperView(to: imageContainerView)
}
Any help would be appreciated!
You need to set the height constraint for UITextView(). Because you are giving fixed top anchor and and bottom anchor so it stretches the textview.
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
I have an iOS project I'm working on using Xcode 9.2 and Swift4. I have a UITextView but second the UITextView not visible.
let logo: UIImageView = {
let imageView = UIImageView(image: #imageLiteral(resourceName: "logoyeni"));
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
let bannerText: UITextView = {
let textView = UITextView()
textView.text = "Müziğin Sosyal Medyası";
textView.textColor = UIColor.salmon;
textView.textAlignment = .center
textView.isEditable = false
textView.translatesAutoresizingMaskIntoConstraints = false
textView.isScrollEnabled = false
return textView
}()
let slogan: UITextView = {
let textView1 = UITextView()
textView1.text = "Lorem Ipsum Dolor Sit Amet Consectetur";
//textView.font = UIFont.textStyle3;
//textView.textColor = UIColor.cloudyBlue;
textView1.textAlignment = .center;
textView1.isEditable = false
textView1.translatesAutoresizingMaskIntoConstraints = false;
return textView1
}()
ViewDidLoad =
super.viewDidLoad()
view.addSubview(logo);
view.addSubview(bannerText);
view.addSubview(slogan);
setup();
and constrains =
private func setup(){
logo.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
logo.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
logo.widthAnchor.constraint(equalToConstant: 127).isActive = true
logo.heightAnchor.constraint(equalToConstant: 127).isActive = true
bannerText.topAnchor.constraint(equalTo: logo.bottomAnchor, constant: 29).isActive = true
bannerText.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
bannerText.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
bannerText.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
slogan.topAnchor.constraint(equalTo: bannerText.bottomAnchor, constant: 4).isActive = true
slogan.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
slogan.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
slogan.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
}
How to fix this problem?
Your constraints are wrong.
bannerText.bottomAnchor = view.bottomAnchor
slogan.topAnchor = bannerText.bottomAnchor(view.bottomAnchor)
slogan.bottomAnchor = view.bottomAnchor
This will set the height of the second text view to zero.
You should have a height constraint for at least one of the text views rather than pinning both of them to the bottom of the superView.
You have pinned the bannerText to the bottom of the logo (offset by 29) and the left, right and bottom of the view.
You have then pinned the slogan to the bottom of the bannerText (offset by 4) and the left, right and bottom of the view.
That's not going to work because the bottom of the bannerText and the bottom of the view are the same and as the top of the slogan is the bottom of the view the top of slogan ends up being the bottom of the view and therefore it is outside the view (the constraints probably break as well).
You need to fix the constraints based on what you want to see.
I have a programmatic view with a label inside of it that I'm pinning to the bottom of the navBar. There will be dynamic text inside of the label and I want the view the label is in to be the at least 64 pts or bigger if the height of text makes it smaller.
The intrinsic size of the text from this label sets the view at a noticeable height.
setViewAndLabel(dynamicText: "Unknown Error\nPlease try your request again\Error: 123")
However the intrinsic size of this text makes the height to small:
setViewAndLabel(dynamicText: "Message Deleted!")
The Message Deleted! should be more along the lines of:
I used some return keys to set it like that but I don't think that's the correct way to go because different messages will get generated:
setViewAndLabel(dynamicText: "\nMessage Deleted!\n")
I also tried:
if myView.heightAnchor.constraint(lessThanOrEqualToConstant: 64){
myView.heightAnchor.constraint(equalToConstant: 64).isActive = true
}
But I get the error:
'NSLayoutConstraint' is not convertible to 'Bool'
What's the best way to set the height for the view the label is in to a minimum height?
var myLabel: UILabel(){
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.white
label.font = UIFont(name: "Helvetica-Regular", size: 19)
label.numberOfLines = 0
label.sizeToFit()
label.textAlignment = .center
return label
}
let myView:UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewWillAppear(_ animated: Bool)
super.viewWillAppear(animated){
setViewAndLabel(dynamicText: //some text will get set here)
}
func setViewAndLabel(dynamicText: String){
view.addSubView(myView)
myView.backgroundColor = UIColor.red
myView.topAnchor.constraint(equalTo: view.topAnchor, constant: 64).isActive = true
View.widthAnchor.constraint(equalTo: view.widthAnchor, constant: 0).isActive = true
myView.addSubView(myLabel)
myLabel.text = dynamicText
myLabel.topAnchor.constraint(equalTo: myView.topAnchor, constant: 0).isActive = true
myLabel.widthAnchor.constraint(equalTo: myView.widthAnchor, constant: 0).isActive = true
myView.bottomAnchor.constraint(equalTo: myLabel.bottomAnchor, constant: 0).isActive = true
//this if statement doesn't work
if myView.heightAnchor.constraint(lessThanOrEqualToConstant: 64){
viewForErrorLabel.heightAnchor.constraint(equalToConstant: 64).isActive = true
}
}
This is how you have to set up your constraints:
view.addSubview(myView)
myView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
myView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
myView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
myView.heightAnchor.constraint(greaterThanOrEqualToConstant: 64).isActive = true
myView.addSubview(myLabel)
myLabel.topAnchor.constraint(equalTo: myView.topAnchor).isActive = true
myLabel.leadingAnchor.constraint(equalTo: myView.leadingAnchor).isActive = true
myLabel.trailingAnchor.constraint(equalTo: myView.trailingAnchor).isActive = true
myLabel.bottomAnchor.constraint(equalTo: myView.bottomAnchor).isActive = true
You do not need to check the label's height at all. You can simply always create that height greater than or equal constraint for myView and its height will never be smaller than 64pt (or whatever value you set it to) - even if the label contains a very short text.
I have the following setup: A UIView adds a bunch of subviews (UILabels) programmatically, and sets also the autolayout constraints, so that the distance between the labels and between the UIViews edges is 10 each. The goal is that the UIView sets its size accordingly to the content of all the subviews (labels with dynamic text) including the spaces.
I use the following code, but it seems not to work. The UIView doesn't resize, it shrinks the labels.
// setup of labelList somewhere else, containing the label data
var lastItemLabel: UILabel? = nil
var i = 1
for item in itemList {
let theLabel = UILabel()
// ... label setup with text, fontsize and color
myView.addSubview(theLabel)
theLabel.translatesAutoresizingMaskIntoConstraints = false
// If it is the second or more
if let lastLabel = lastItemLabel {
theLabel.leadingAnchor.constraint(equalTo: lastLabel.trailingAnchor, constant: 12).isActive = true
theLabel.topAnchor.constraint(equalTo: myView.topAnchor, constant: 10).isActive = true
// if it is the last label
if i == labelList.count {
theLabel.trailingAnchor.constraint(equalTo: myView.trailingAnchor, constant: 12).isActive = true
}
}
// If it is the first label
else {
theLabel.leadingAnchor.constraint(equalTo: myView.leadingAnchor, constant: 12).isActive = true
theLabel.topAnchor.constraint(equalTo: myView.topAnchor, constant: 10).isActive = true
}
lastItemLabel = theLabel
i += 1
}
Since you need your content to be larger than the physical display of the device, you will need to add a UIScrollView to contain your labels.