I have created a custom view which has an image and a label. I added a tap gesture on it, so when user taps on the view, both label and custom view expands to show the details/more text on the label.
I added code to expand the label which works fine, but the UIView/custom view doesn't get expanded. Below is my code to expand the label.
How do I expand the custom view either programmatically or by adding any constraints?
#objc func bannerTapped(_ sender:UITapGestureRecognizer){
self.bannerMessageLabel.numberOfLines = 0
self.bannerMessageLabel.text = "Unicode characters take up multiple GSM characters. When a Unicode symbol appears in a text, it is usually segmented at the 70-character mark, thus making it even harder for the recipient to decipher the message."
self.bannerMessageLabel.sizeToFit()
self.bannerView.setNeedsLayout()
self.bannerView.layoutIfNeeded()
}
Attaching image. I don't have any constraints on them since I am leaving it on Stack view to handle it.
I created a sample ViewController that does what you need. I think what you were missing is the following anchor:
bannerView.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor).isActive = true
Sample View Controller:
class TestViewController: UIViewController {
// MARK: - UIObjects
private let bannerView: UIView = {
let bannerView = UIView()
bannerView.translatesAutoresizingMaskIntoConstraints = false
bannerView.backgroundColor = .white
return bannerView
}()
private let stackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical // Works for both horizontal and vertical axis
return stackView
}()
private let bannerWarningImage: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "arrow-right")
imageView.backgroundColor = .orange
return imageView
}()
private let bannerMessageLabel: UILabel = {
let label = UILabel()
label.text = "Unicode characters take up multiple GSM characters. When a Unicode symbol appears in a text"
label.numberOfLines = 0
label.backgroundColor = .green
return label
}()
// MARK: - Overrides
override func viewDidLoad() {
super.viewDidLoad()
bannerView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(bannerTapped)))
addUIElements()
addConstrains()
}
// MARK: - UISetup
private func addUIElements() {
view.backgroundColor = .purple
view.addSubview(bannerView)
bannerView.addSubview(stackView)
stackView.addArrangedSubview(bannerWarningImage)
stackView.addArrangedSubview(bannerMessageLabel)
}
private func addConstrains() {
bannerView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
bannerView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
bannerView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
bannerView.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: bannerView.topAnchor).isActive = true
// The -10 for you to see that the bannerView height is actually chaning
stackView.bottomAnchor.constraint(equalTo: bannerView.bottomAnchor, constant: -10).isActive = true
stackView.leadingAnchor.constraint(equalTo: bannerView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: bannerView.trailingAnchor).isActive = true
}
// MARK: - Actions
#objc private func bannerTapped() {
self.bannerMessageLabel.text = "Unicode characters take up multiple GSM characters. When a Unicode symbol appears in a text, it is usually segmented at the 70-character mark, thus making it even harder for the recipient to decipher the message."
}
}
Related
I have one textField.
private var verseTitle: UITextField = {
let tf = UITextField()
tf.placeholder = "TITLE"
tf.font = UIFont(suite16: .tBlackItalic, size: 18)
tf.textColor = .black.withAlphaComponent(0.5)
tf.returnKeyType = .done
tf.translatesAutoresizingMaskIntoConstraints = false
return tf
}()
In viewDidLoad method, I have assigned self as delegate.
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
verseTitle.delegate = self
}
In viewDidLayout method, I'm using stack view to add textField to the view.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
//Title
let titleStack = UIStackView()
titleStack.axis = .horizontal
titleStack.alignment = .center
titleStack.distribution = .equalSpacing
titleStack.spacing = 8
titleStack.addArrangedSubview(verseTitle)
titleStack.addArrangedSubview(floorView)
titleStack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(titleStack)
NSLayoutConstraint.activate([
floorView.heightAnchor.constraint(equalToConstant: 13),
floorView.widthAnchor.constraint(equalToConstant: 13),
titleStack.centerXAnchor.constraint(equalTo: view.centerXAnchor),
titleStack.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
])
}
Now, the problem I'm facing is, that when I start typing in the textField, the keyboard gets dismissed only after I type one letter. I'm not sure why this is happening. I have to tap on the field after entering each letter. For some reason, the focus is taken away from the field after each letter is entered (unless I tap on a suggested autocorrect - the whole string is correctly added to the string at once)
What's going on here is your view, including the UITextField gets re-created after each keystroke because your view construction is in: viewDidLayoutSubviews(), When the bounds change for a view controller's view, the view adjusts the positions of its subviews and then the system calls this method.
To fix it, move the code to ViewDidLoad, so the view is created only once:
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
verseTitle.delegate = self
let titleStack = UIStackView()
titleStack.axis = .horizontal
titleStack.alignment = .center
titleStack.distribution = .equalSpacing
titleStack.spacing = 8
titleStack.addArrangedSubview(verseTitle)
titleStack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(titleStack)
NSLayoutConstraint.activate([
titleStack.centerXAnchor.constraint(equalTo: view.centerXAnchor),
titleStack.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
])
}
Embedd StackView in ScrollView that is embedded in a main StackView
I am having trouble with a rather complicated detail view that I want to do programmatically. My view hierarchy looks something like this:
Since this might be better explained visualising, I have a screenshot here:
My problem is that I don't know how to set the height constraint on descriptionTextView – right now it's set to 400. What I want though is that it takes up all the space available as the middle item of the main stack view. Once one or more comments are added to the contentStackView, the text field should shrink.
I am not sure which constraints for which views I must set to achieve this...
Here's my take on it so far:
import UIKit
class DetailSampleViewController: UIViewController {
lazy var mainStackView: UIStackView = {
let m = UIStackView()
m.axis = .vertical
m.alignment = .fill
m.distribution = .fill
m.spacing = 10
m.translatesAutoresizingMaskIntoConstraints = false
m.addArrangedSubview(titleTextField)
m.addArrangedSubview(contentScrollView)
m.addArrangedSubview(footerStackView)
return m
}()
lazy var titleTextField: UITextField = {
let t = UITextField()
t.borderStyle = .roundedRect
t.placeholder = "Some Fancy Placeholder"
t.text = "Some Fancy Title"
t.translatesAutoresizingMaskIntoConstraints = false
return t
}()
lazy var contentScrollView: UIScrollView = {
let s = UIScrollView()
s.contentMode = .scaleToFill
s.keyboardDismissMode = .onDrag
s.translatesAutoresizingMaskIntoConstraints = false
s.addSubview(contentStackView)
return s
}()
lazy var contentStackView: UIStackView = {
let s = UIStackView()
s.translatesAutoresizingMaskIntoConstraints = false
s.axis = .vertical
s.alignment = .fill
s.distribution = .equalSpacing
s.spacing = 10
s.contentMode = .scaleToFill
s.addArrangedSubview(descriptionTextView)
s.addArrangedSubview(getCommentLabel(with: "Some fancy comment"))
s.addArrangedSubview(getCommentLabel(with: "Another fancy comment"))
s.addArrangedSubview(getCommentLabel(with: "And..."))
s.addArrangedSubview(getCommentLabel(with: "..even..."))
s.addArrangedSubview(getCommentLabel(with: "...more..."))
s.addArrangedSubview(getCommentLabel(with: "...comments..."))
s.addArrangedSubview(getCommentLabel(with: "Some fancy comment"))
s.addArrangedSubview(getCommentLabel(with: "Another fancy comment"))
s.addArrangedSubview(getCommentLabel(with: "And..."))
s.addArrangedSubview(getCommentLabel(with: "..even..."))
s.addArrangedSubview(getCommentLabel(with: "...more..."))
s.addArrangedSubview(getCommentLabel(with: "...comments..."))
return s
}()
lazy var descriptionTextView: UITextView = {
let tv = UITextView()
tv.font = UIFont.systemFont(ofSize: 17.0)
tv.clipsToBounds = true
tv.layer.cornerRadius = 5.0
tv.layer.borderWidth = 0.25
tv.translatesAutoresizingMaskIntoConstraints = false
tv.text = """
Some fancy textfield text,
spanning over multiple
lines
...
"""
return tv
}()
lazy var footerStackView: UIStackView = {
let f = UIStackView()
f.axis = .horizontal
f.alignment = .fill
f.distribution = .fillEqually
let commentLabel = UILabel()
commentLabel.text = "Comments"
let addCommentButton = UIButton(type: UIButton.ButtonType.system)
addCommentButton.setTitle("Add Comment", for: .normal)
f.addArrangedSubview(commentLabel)
f.addArrangedSubview(addCommentButton)
return f
}()
override func loadView() {
view = UIView()
view.backgroundColor = . systemBackground
navigationController?.isToolbarHidden = true
view.addSubview(mainStackView)
NSLayoutConstraint.activate([
mainStackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 12),
mainStackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -12),
mainStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 12),
mainStackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -12),
titleTextField.heightAnchor.constraint(equalToConstant: titleTextField.intrinsicContentSize.height),
contentStackView.leadingAnchor.constraint(equalTo: contentScrollView.leadingAnchor),
contentStackView.trailingAnchor.constraint(equalTo: contentScrollView.trailingAnchor),
contentStackView.topAnchor.constraint(equalTo: contentScrollView.topAnchor),
contentStackView.bottomAnchor.constraint(equalTo: contentScrollView.bottomAnchor),
descriptionTextView.heightAnchor.constraint(equalToConstant: 400),
descriptionTextView.leadingAnchor.constraint(equalTo: mainStackView.leadingAnchor),
descriptionTextView.trailingAnchor.constraint(equalTo: mainStackView.trailingAnchor),
])
}
override func viewDidLoad() {
super.viewDidLoad()
title = "Detail View"
}
func getCommentLabel(with text: String) -> UILabel {
let l = UILabel()
l.layer.borderWidth = 0.25
l.translatesAutoresizingMaskIntoConstraints = false
l.text = text
return l
}
}
You're close, but a couple notes:
When using stack views - particularly inside scroll views - you sometimes need to explicitly define which elements can be stretched or not, and which elements can be compressed or not.
To get the scroll view filled before it has enough content, you need to set constraints so the combined content height is equal to the scroll view frame's height, but give that constraint a low priority so auto-layout can "break" it when you have enough vertical content.
A personal preference: I'm generally not a fan of adding subviews inside lazy var declarations. It can become confusing when trying to setup constraints.
I've re-worked your posted code to at least get close to what you're going for. It starts with NO comment labels... tapping the "Add Comment" button will add "numbered comment labels" and every third comment will wrap onto multiple lines.
Not really all that much in the way of changes... and I think I added enough comments to make things clear.
class DetailSampleViewController: UIViewController {
lazy var mainStackView: UIStackView = {
let m = UIStackView()
m.axis = .vertical
m.alignment = .fill
m.distribution = .fill
m.spacing = 10
m.translatesAutoresizingMaskIntoConstraints = false
// don't add subviews here
return m
}()
lazy var titleTextField: UITextField = {
let t = UITextField()
t.borderStyle = .roundedRect
t.placeholder = "Some Fancy Placeholder"
t.text = "Some Fancy Title"
t.translatesAutoresizingMaskIntoConstraints = false
return t
}()
lazy var contentScrollView: UIScrollView = {
let s = UIScrollView()
s.contentMode = .scaleToFill
s.keyboardDismissMode = .onDrag
s.translatesAutoresizingMaskIntoConstraints = false
// don't add subviews here
return s
}()
lazy var contentStackView: UIStackView = {
let s = UIStackView()
s.translatesAutoresizingMaskIntoConstraints = false
s.axis = .vertical
s.alignment = .fill
// distribution needs to be .fill (not .equalSpacing)
s.distribution = .fill
s.spacing = 10
s.contentMode = .scaleToFill
// don't add subviews here
return s
}()
lazy var descriptionTextView: UITextView = {
let tv = UITextView()
tv.font = UIFont.systemFont(ofSize: 17.0)
tv.clipsToBounds = true
tv.layer.cornerRadius = 5.0
tv.layer.borderWidth = 0.25
tv.translatesAutoresizingMaskIntoConstraints = false
tv.text = """
Some fancy textfield text,
spanning over multiple lines.
This textView now has a minimum height of 160-pts.
"""
return tv
}()
lazy var footerStackView: UIStackView = {
let f = UIStackView()
f.axis = .horizontal
f.alignment = .fill
f.distribution = .fillEqually
let commentLabel = UILabel()
commentLabel.text = "Comments"
let addCommentButton = UIButton(type: UIButton.ButtonType.system)
addCommentButton.setTitle("Add Comment", for: .normal)
// add a target so we can add comment labels
addCommentButton.addTarget(self, action: #selector(addCommentLabel(_:)), for: .touchUpInside)
// don't allow button height to be compressed
addCommentButton.setContentCompressionResistancePriority(.required, for: .vertical)
f.addArrangedSubview(commentLabel)
f.addArrangedSubview(addCommentButton)
return f
}()
// just for demo - numbers the added comment labels
var commentIndex: Int = 0
// do all this in viewDidLoad(), not in loadView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = . systemBackground
navigationController?.isToolbarHidden = true
title = "Detail View"
// add the mainStackView
view.addSubview(mainStackView)
// add elements to mainStackView
mainStackView.addArrangedSubview(titleTextField)
mainStackView.addArrangedSubview(contentScrollView)
mainStackView.addArrangedSubview(footerStackView)
// add contentStackView to contentScrollView
contentScrollView.addSubview(contentStackView)
// add descriptionTextView to contentStackView
contentStackView.addArrangedSubview(descriptionTextView)
// tell contentStackView to be the height of contentScrollView frame
let contentStackHeight = contentStackView.heightAnchor.constraint(equalTo: contentScrollView.frameLayoutGuide.heightAnchor)
// but give it a lower priority do it can grow as comment labels are added
contentStackHeight.priority = .defaultLow
NSLayoutConstraint.activate([
// constrain mainStackView top / bottom / leading / trailing to safe area
mainStackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 12),
mainStackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -12),
mainStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 12),
mainStackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -12),
// title text field
titleTextField.heightAnchor.constraint(equalToConstant: titleTextField.intrinsicContentSize.height),
// minimum height for descriptionTextView
descriptionTextView.heightAnchor.constraint(greaterThanOrEqualToConstant: 160.0),
// constrain contentStackView top / leading / trailing / bottom to contentScrollView
contentStackView.topAnchor.constraint(equalTo: contentScrollView.topAnchor),
contentStackView.leadingAnchor.constraint(equalTo: contentScrollView.leadingAnchor),
contentStackView.trailingAnchor.constraint(equalTo: contentScrollView.trailingAnchor),
contentStackView.bottomAnchor.constraint(equalTo: contentScrollView.bottomAnchor),
// constrain contentStackView width to contentScrollView frame
contentStackView.widthAnchor.constraint(equalTo: contentScrollView.frameLayoutGuide.widthAnchor),
// activate contentStackHeight constraint
contentStackHeight,
])
// during dev, give some background colors so we can see the frames
contentScrollView.backgroundColor = .cyan
descriptionTextView.backgroundColor = .yellow
}
#objc func addCommentLabel(_ sender: Any?) -> Void {
// commentIndex is just used to number the added comments
commentIndex += 1
// let's make every third label end up with multiple lines, just to
// confirm variable-height labels won't mess things up
var s = "This is label \(commentIndex)"
if commentIndex % 3 == 0 {
s += ", and it has enough text that it should need to wrap onto multiple lines, even in landscape orientation."
}
let v = getCommentLabel(with: s)
// don't let comment labels stretch vertically
v.setContentHuggingPriority(.required, for: .vertical)
// don't let comment labels get compressed vertically
v.setContentCompressionResistancePriority(.required, for: .vertical)
contentStackView.addArrangedSubview(v)
// auto-scroll to bottom to show newly added comment label
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
let r = CGRect(x: 0.0, y: self.contentScrollView.contentSize.height - 1.0, width: 1.0, height: 1.0)
self.contentScrollView.scrollRectToVisible(r, animated: true)
}
}
func getCommentLabel(with text: String) -> UILabel {
let l = UILabel()
l.layer.borderWidth = 0.25
l.translatesAutoresizingMaskIntoConstraints = false
l.text = text
// allow wrapping / multi-line comments
l.numberOfLines = 0
return l
}
}
In SwiftUI, you could easily achieving padding via the following code:
Text("Hello World!")
.padding(20)
What options do I have to achieve the same in UIKit?
You can use storyboard for same and easily make this layout by combining UIView and Label.
Just add once view in view controller and centre align vertically and horizontally. Then add label inside that view and set top, bottom, leading, trailing to 20 respective to that view.
Just check following image.
This will make exactly same output as you want.
Using a UIStackView, something like this:
class PaddingableView: UIView {
var padding = UIEdgeInsets.zero { didSet { contentStackView.layoutMargins = padding } }
// MARK: Subviews
private lazy var contentStackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.alignment = .fill
stackView.distribution = .fill
stackView.isLayoutMarginsRelativeArrangement = true
stackView.layoutMargins = padding
return stackView
}()
// MARK: Functions
private func addContentStackView() {
super.addSubview(contentStackView)
NSLayoutConstraint.activate([
contentStackView.topAnchor.constraint(equalTo: topAnchor),
contentStackView.bottomAnchor.constraint(equalTo: bottomAnchor),
contentStackView.leadingAnchor.constraint(equalTo: leadingAnchor),
contentStackView.trailingAnchor.constraint(equalTo: trailingAnchor)
])
}
override func addSubview(_ view: UIView) {
contentStackView.addArrangedSubview(view)
}
override func layoutSubviews() {
if contentStackView.superview == nil { addContentStackView() }
super.layoutSubviews()
}
}
Note that using too many nested stackviews can hit the performance
Ok, most simple is of corse Storyboard-based approach (as shown by #Sagar_Chauhan)...
Below is provided line-by-line Playground variant (as to compare with SwiftUI Preview by lines of code, for instance).
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let view = UIView(frame: CGRect(x: 0, y: 0, width: 375, height: 677))
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
self.view = view
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let label = UILabel(frame: .zero)
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Hello World!"
label.textColor = .black
label.backgroundColor = .yellow
label.textAlignment = .center
self.view.addSubview(label)
let fitSize = label.sizeThatFits(view.bounds.size)
label.widthAnchor.constraint(equalToConstant: fitSize.width + 12.0).isActive = true
label.heightAnchor.constraint(equalToConstant: fitSize.height + 12.0).isActive = true
label.topAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
label.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
}
}
PlaygroundPage.current.liveView = MyViewController()
Here is my simple example. I have 1 vertical stack view with 1 subview. I want that subviews height to be based on the intrinsic height of the label within it, so that I can maintain a dynamic height for the entire stack view. How can this be done? Thanks
I think you did it right. But here is the keys:
Don't set height for stackView.
Set label top, bottom, left, trailing constraint to view.
Run. It should be okay on simulator.
If you found label's height seems not wrapping (neither both on storyboard or simulator), then change label's Vertical Content Hugging Priority to 750.
Try this code:
class DyanmicTextLabelViewController: UIViewController {
private var didAddConstraint = false
private let label: UILabel = {
let view = UILabel()
view.translatesAutoresizingMaskIntoConstraints = false
view.setContentHuggingPriority(.required, for: .vertical)
view.setContentCompressionResistancePriority(.required, for: .vertical)
view.text = "Layout anchors let you create constraints in an easy-to-read, compact format. They expose a number of methods for creating different types of constraints, as shown in Listing 13-1."
view.numberOfLines = 0
return view
}()
private lazy var container: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
view.backgroundColor = .red
return view
}()
private lazy var stackview : UIStackView = {
let view = UIStackView()
view.translatesAutoresizingMaskIntoConstraints = false
view.axis = .horizontal
view.distribution = .fill
view.addArrangedSubview(container)
return view
}()
override func loadView() {
super.loadView()
view.addSubview(stackview)
view.setNeedsUpdateConstraints()
view.backgroundColor = .white
}
override func updateViewConstraints() {
super.updateViewConstraints()
if didAddConstraint == false {
didAddConstraint = true
// stackview constraints
stackview.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
let topAnchor = stackview.topAnchor.constraint(equalTo: view.topAnchor)
topAnchor.constant = 20
topAnchor.isActive = true
stackview.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
// label constraint
// example for giving label a left padding
let labelLeft = label.leftAnchor.constraint(equalTo: container.leftAnchor)
labelLeft.constant = 16.0
labelLeft.isActive = true
label.topAnchor.constraint(equalTo: container.topAnchor).isActive = true
label.rightAnchor.constraint(equalTo: container.rightAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: container.bottomAnchor).isActive = true
}
}
}
The important part here is the initialization of stackview, label & constraint set on label
label initialization
private let label: UILabel = {
let view = UILabel()
view.translatesAutoresizingMaskIntoConstraints = false
view.setContentHuggingPriority(.required, for: .vertical)
view.setContentCompressionResistancePriority(.required, for: .vertical)
view.text = "Layout anchors let you create constraints in an easy-to-read, compact format. They expose a number of methods for creating different types of constraints, as shown in Listing 13-1."
view.numberOfLines = 0
return view
}()
stackview initialization
private lazy var stackview : UIStackView = {
let view = UIStackView()
view.translatesAutoresizingMaskIntoConstraints = false
view.axis = .horizontal
view.distribution = .fill
view.addArrangedSubview(container)
return view
}()
label constraint
// label constraint
// example for giving label a left padding
let labelLeft = label.leftAnchor.constraint(equalTo: container.leftAnchor)
labelLeft.constant = 16.0
labelLeft.isActive = true
label.topAnchor.constraint(equalTo: container.topAnchor).isActive = true
label.rightAnchor.constraint(equalTo: container.rightAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: container.bottomAnchor).isActive = true
This settings could be easily translated to storyboard.
I'd like to create a simple app without Storyboard. I've created a closure outside the viewDidLoad method, which represents a title on the screen. My problem is that the code contains duplicated lines view.addSubview(label) and it positions the label to the wrong place.
Could you please help me solving this issue?
class HomeVC: UIViewController {
let titleLabel: UILabel = {
let view = UIView()
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
label.text = "Hello"
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(titleLabel)
}
}
I think you want to set the label at the center of HomeVC's view, the problem in the above code is that you are making a new view and place the label inside the view and thats not what you want , so
You just make label first like this:
let titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Hello"
return label
}()
and then in viewDidLoad add this label as subview of view and apply constraints
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(titleLabel)
setupTitleLabel()
}
func setupTitleLabel() {
titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
titleLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
//you also need to give the label height and width constraints to label here...
}