Label Not Resizing Within Container Despite Several Methods - swift

I have a reusable view class that I call when I want to add a disappearing subView to another view. I have a UILabel extension to determine when there is to much text for the label's current size(this extension works), and within this closure I'm trying to expand the: contianerView(regView)'s height, the label's height, and the label's height anchor, since the label was created programatically. As you guessed, the label isn't expandng correctly.
I've tried setting the numberOfLines to 0; changing the label's frame; using .layoutSubviews; removing when the height anchor was originally set, so now it's only called when the resize view method is used.
Label extension:
extension UILabel {
var isTruncated: Bool {
guard let labelText = text else {
return false
}
let labelTextSize = (labelText as NSString).boundingRect(
with: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude),
options: .usesLineFragmentOrigin,
attributes: [.font: font],
context: nil).size
return labelTextSize.height > bounds.size.height
}
}
function to add reusable view(most of it is within the while loop towards the bottom):
func addDisapearingView(toview: UIView, text: String, textColor: UIColor, colorView: UIColor, alpha: CGFloat, height: CGFloat){
regView.backgroundColor = colorView
regView.alpha = alpha
toview.addSubview(regView)
regView.translatesAutoresizingMaskIntoConstraints = false
if #available(iOS 11.0, *) {
let guide = toview.safeAreaLayoutGuide
regView.trailingAnchor.constraint(equalTo: guide.trailingAnchor).isActive = true
regView.leadingAnchor.constraint(equalTo: guide.leadingAnchor).isActive = true
regView.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
UIView.animate(withDuration: 5.0) {
self.regView.heightAnchor.constraint(equalToConstant: height).isActive = true
}
} else {
NSLayoutConstraint(item: regView,
attribute: .top,
relatedBy: .equal,
toItem: toview, attribute: .top,
multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint(item: regView,
attribute: .leading,
relatedBy: .equal, toItem: toview,
attribute: .leading,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: regView, attribute: .trailing,
relatedBy: .equal,
toItem: toview,
attribute: .trailing,
multiplier: 1.0,
constant: 0).isActive = true
regView.heightAnchor.constraint(equalToConstant: height).isActive = true
}
let label = UILabel(frame: CGRect(x: regView.frame.origin.x, y: regView.frame.origin.y, width: regView.bounds.width, height: regView.bounds.height))
label.numberOfLines = 0
label.adjustsFontSizeToFitWidth = true
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.translatesAutoresizingMaskIntoConstraints = false
label.center.x = newView.center.x
label.center.y = newView.center.y
label.textAlignment = .center
label.text = text
label.textColor = textColor
regView.addSubview(label)
if label.isTruncated {
print("LABEL IS TRUNCATED")
}
//test if there is more text than the label has room for
while label.isTruncated {
print("printing while truncating in the wHiLE loop")
regView.bounds.size.height += 5
label.bounds.size.height += 5
var currentLabelHeight = label.bounds.height
let amt = currentLabelHeight + 5
label.frame = CGRect(x: regView.frame.origin.x, y: regView.frame.origin.y, width: regView.bounds.width, height: CGFloat(amt))
var heighT : CGFloat = height
heighT += 5
regView.heightAnchor.constraint(equalToConstant: heighT).isActive = true
}
regView.layoutSubviews()
label.sizeToFit()
//remove
Timer.scheduledTimer(withTimeInterval: 2.8, repeats: false) { (action) in
UIView.animate(withDuration: 2.8, animations: {
self.regView.heightAnchor.constraint(equalToConstant: 0).isActive = true
label.heightAnchor.constraint(equalToConstant: 0).isActive = true
})
}
}
I've briefly done this before in storyboard where I had to expand a label within another view when the text was too long(this time it did work!), and the important part there was editing the height constraint, so I think this might have something to do with modifying the height constraint.
Any help would be greatly appreciated!
ANSWER:
I asked another question here: Programatically Created Label Within Container View Won't Expand For Text
it has the same code here and everything in the question but the answer works.

If i understand correct, you have your view and a label, and you want your view to dynamically change height depend on label content. I suggest you to break that task to chunks and resolve it step by step.
1 - You might want to add a test UIView object instead of label with fixed size. When u do this, you will see whether you parent view expand depending of test view size.
2 - If it is, you are up to create a label with height you need. All that you need to know its font, text and width. I think this link may help you. After you sure, that your label size is correct (you may want to print it out) you may add it as any other UIView object to your parent view.

Related

Why is my second button behaving different when using auto layout constraints?

I can not figure out why my button is behaving different when using auto layout constraints programmatically.
When setting the view that holds my cancelBtn every thing is working fine:
let cancelBtnView = TriangularView(frame: CGRect(x: 0, y: 0, width: 300, height: 150))
let cancelBtn = UIButton()
cancelBtnView.addSubview(cancelBtn)
cancelBtn.titleLabel!.font = UIFont.fontAwesome(ofSize: 35, style: .regular)
cancelBtn.setTitle(String.fontAwesomeIcon(name: .times), for: .normal)
cancelBtn.frame = CGRect(x: cancelBtnView.bounds.width / 16, y: cancelBtnView.bounds.height / 2 ,width: 55, height: 55)
I get the following layout:
When setting up the view for my doneBtn I get a different output:
let doneBtnView = TriangularView()
doneBtnView.translatesAutoresizingMaskIntoConstraints = false
doneBtnView.widthAnchor.constraint(equalToConstant: 300).isActive = true
doneBtnView.heightAnchor.constraint(equalToConstant: 150).isActive = true
doneBtnView.trailingAnchor.constraint(equalTo: actionButtonView.trailingAnchor).isActive = true
doneBtnView.bottomAnchor.constraint(equalTo: actionButtonView.bottomAnchor).isActive = true
let doneBtn = UIButton()
doneBtnView.addSubview(doneBtn)
doneBtn.titleLabel!.font = UIFont.fontAwesome(ofSize: 35, style: .regular)
doneBtn.setTitle(String.fontAwesomeIcon(name: .check), for: .normal)
doneBtn.translatesAutoresizingMaskIntoConstraints = false
doneBtn.widthAnchor.constraint(equalToConstant: 55).isActive = true
doneBtn.heightAnchor.constraint(equalToConstant: 55).isActive = true
doneBtn.leadingAnchor.constraint(equalTo: doneBtnView.leadingAnchor, constant: doneBtnView.bounds.width / 16).isActive = true
doneBtn.bottomAnchor.constraint(equalTo: doneBtnView.bottomAnchor, constant: doneBtnView.bounds.height / 2).isActive = true
Setting the constraints for my doneBtn programmatically I get the following:
The constraints for the doneBtnView are set programmatically because I want to pin it to the bottom right of its superview.
Your problem is that you are looking at the bounds of the other view when setting your constraint for your doneBtn, but those bounds will not have been set yet because layout has not happened. In general, it is a bad idea to combine frame/bounds and constraints.
This can be done, but not with anchors. Try the following NSLayoutConstraints:
NSLayoutConstraint(item: doneBtn, attribute: .leading, relatedBy: .equal,
toItem: doneBtnView, attribute: .trailing, multiplier: 1/16, constant: 0).isActive = true
NSLayoutConstraint(item: doneBtn, attribute: .top, relatedBy: .equal,
toItem: doneBtnView, attribute: .bottom, multiplier: 1/2, constant: 0).isActive = true
and then set the width and height using anchors:
doneBtn.widthAnchor.constraint(equalToConstant: 55).isActive = true
doneBtn.heightAnchor.constraint(equalToConstant: 55).isActive = true
Note: Because we're using the .trailing and .bottom constraints to represent width and height, it might be necessary to put doneBtnView into a container view of the same size because the values will be in the coordinate system of the parent view. By making the parent view the exact same size, width will be equal to the trailing constraint, and height will be equal to the bottom constraint.

Dynamic content to uiscrollview with autolayout not working as expected

I am trying to add dynamic content to the scrollview as below
for i in 0 ..< 4 {
let gameView = Card.instantiate()
gameView.frame.origin.y = gameView.frame.size.height * CGFloat(i)
contentView.addSubview(gameView)
gameView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
let widthConstraint = NSLayoutConstraint(item: gameView, attribute: .width, relatedBy: .equal,toItem: contentView, attribute: .width, multiplier: 0.8,constant : 0.0)
contentView.addConstraint(widthConstraint)
let heightConstraint = NSLayoutConstraint(item: gameView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1.0, constant: 100)
gameView.addConstraint(heightConstraint)
contentView.frame.size.height = contentView.frame.size.height + gameView.frame.size.height
scrollView.contentSize.height = scrollView.contentSize.height + gameView.frame.size.height
}
Cardview is defined xib with autolayout
And view structure in IB as below SuperView > ScrollView > ContentView > DynamicViews
ContentView Also contain some static contents like buttons and labels.Dynamic View is below that static contents
And the output screen is look like below but which is not proper-aligned
Is this the correct approach to add dynamic views to scrollview?
This is an ideal case for UIStackView
The stack view will handle vertical positioning and width of the instantiated gameViews (assuming you have your xib loading set up correctly).
By constraining the top, leading, trailing, bottom and width of the stack view to the scrollView, it will also define the "scrollable area" (the .contentSize).
All done with auto-layout:
let sv = UIStackView()
sv.axis = .vertical
sv.alignment = .fill
sv.distribution = .fill
sv.spacing = 0 // change to add vertical spacing if desired
sv.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(sv)
NSLayoutConstraint.activate([
sv.topAnchor.constraint(equalTo: scrollView.topAnchor),
sv.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
sv.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
sv.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
sv.widthAnchor.constraint(equalTo: scrollView.widthAnchor, multiplier: 1.0),
])
for _ in 0 ..< 4 {
let gameView = Card.instantiate()
gameView.translatesAutoresizingMaskIntoConstraints = false
gameView.heightAnchor.constraint(equalToConstant: 100.0).isActive = true
sv.addArrangedSubview(gameView)
}

Programatically Created Label Within Container View Won't Expand For Text

I have a reusable view class, with the function .addDisapearingView() when added to another view displays the text in the functions parameters. Both the label and its container view are programmatically created. When there's long text in the label, I want the label, and the view to both grow in height. When there's text too long for the label, the label doesn't grow-and the text, subsequently, doesn't clip/go to next line. I'm trying to get the container view to expand programmatically based upon the text.
I've tried an extension that detects when the label is truncated. Using that extension, I used the += operator on the label and view to expand both of them with no luck.
while label.isTruncated {
print("printing while truncating in the while loop")
regView.frame.size.height += 5
label.frame.size.height += 5
}
The interesting thing with that is, I've used that code before, with the addition of adding 5 to the height constraint of the view in the storyboard to expand the size of the label for text, and it worked. That lead me to believe that my problem might reside somewhere in editing the height constraint for the regView.
I've tried countless variations of
label.adjustsFontSizeToFitWidth = true
label.numberOfLines = 3
label.lineBreakMode = .byWordWrapping
label.translatesAutoresizingMaskIntoConstraints = false
label.frame.size.height = regView.frame.size.height
label.sizeToFit()
regView.layoutSubviews()
I've tried changing the frame of the view and label, changing the constaints at the top of the code, and the answers from other questions.
Code:
Truncated Label Extension:
extension UILabel {
var isTruncated: Bool {
guard let labelText = text else {
return false
}
let labelTextSize = (labelText as NSString).boundingRect(
with: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude),
options: .usesLineFragmentOrigin,
attributes: [.font: font],
context: nil).size
return labelTextSize.height > bounds.size.height
}
}
View constraint changer:
extension UIView {
func updateConstraint(attribute: NSLayoutAttribute, constant: CGFloat) -> Void {
if let constraint = (self.constraints.filter{$0.firstAttribute == attribute}.first) {
constraint.constant = constant
self.layoutIfNeeded()
}
}
}
Whole function:
func addDisapearingView(toview: UIView, text: String, textColor: UIColor, colorView: UIColor, alpha: CGFloat, height: CGFloat){
regView.backgroundColor = colorView
regView.alpha = alpha
regView.frame = CGRect(x: toview.bounds.minX, y: toview.bounds.minY, width: toview.frame.size.width, height: height)
toview.addSubview(regView)
regView.translatesAutoresizingMaskIntoConstraints = false
if #available(iOS 11.0, *) {
let guide = toview.safeAreaLayoutGuide
regView.trailingAnchor.constraint(equalTo: guide.trailingAnchor).isActive = true
regView.leadingAnchor.constraint(equalTo: guide.leadingAnchor).isActive = true
regView.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
regView.heightAnchor.constraint(equalToConstant: height).isActive = true
} else {
NSLayoutConstraint(item: regView,
attribute: .top,
relatedBy: .equal,
toItem: toview, attribute: .top,
multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint(item: regView,
attribute: .leading,
relatedBy: .equal, toItem: toview,
attribute: .leading,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: regView, attribute: .trailing,
relatedBy: .equal,
toItem: toview,
attribute: .trailing,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: regView, attribute: NSLayoutAttribute.height, relatedBy: .equal, toItem: toview, attribute: .height, multiplier: 1.0, constant: height).isActive = true
//regView.heightAnchor.constraint(equalToConstant: height).isActive = true
}
let label = UILabel(frame: CGRect(x: regView.frame.origin.x, y: regView.frame.origin.y, width: regView.frame.width, height: height))
label.text = text
label.font = UIFont(name: "Arial", size: 12)
label.textColor = textColor
label.adjustsFontSizeToFitWidth = true
label.numberOfLines = 3
label.lineBreakMode = .byWordWrapping
label.translatesAutoresizingMaskIntoConstraints = false
label.frame.size.height = regView.frame.size.height
label.sizeToFit()
regView.layoutSubviews()
regView.addSubview(label)
print("Label Height: \(label.frame.height)")
print("Reg view height: \(regView.frame.height)")
while label.isTruncated {
print("label is truncated")
regView.frame.size.height += 5
label.frame.size.height += 5
label.updateConstraint(attribute: NSLayoutAttribute.height, constant: regView.frame.height)
label.updateConstraint(attribute: NSLayoutAttribute.width, constant: regView.frame.width)
regView.layoutSubviews()
label.sizeToFit()
print("Label Height: \(label.frame.height)")
print("Reg view height: \(regView.frame.height)")
}
//remove
Timer.scheduledTimer(withTimeInterval: 2.8, repeats: false) { (action) in
UIView.animate(withDuration: 2.8, animations: {
self.regView.removeFromSuperview()
label.removeFromSuperview()
})
}
}
which is called by: ReusableView().addDisapearingView(toview: self.view, text: "Anonymous posts will still show up in your profile page!, more text text to test in teh view that doen't work!", textColor: UIColor.white, colorView: UIColor.darkGray, alpha: 0.9, height: 20)
The interesting thing(That I tried to fix) was that even if the height is set to 40, or a value where two lines of text could fit, the label still doesn't expand/truncate, much less if the height param is 20.
Any help would be greatly appreciated!
I guess you completely need auto-layout and make regView expand according to the label's text without any height constraints
let regView = UIView()
func addDisapearingView(toview: UIView, text: String, textColor: UIColor, colorView: UIColor, alpha: CGFloat, height: CGFloat){
regView.backgroundColor = colorView
regView.alpha = alpha
toview.addSubview(regView)
regView.translatesAutoresizingMaskIntoConstraints = false
if #available(iOS 11.0, *) {
let guide = toview.safeAreaLayoutGuide
NSLayoutConstraint.activate([
regView.trailingAnchor.constraint(equalTo: guide.trailingAnchor),
regView.leadingAnchor.constraint(equalTo: guide.leadingAnchor),
regView.topAnchor.constraint(equalTo: guide.topAnchor),
// regView.bottomAnchor.constraint(equalTo: guide.bottomAnchor).isActive = true
// regView.heightAnchor.constraint(equalToConstant: height).isActive = true
])
} else {
NSLayoutConstraint(item: regView,
attribute: .top,
relatedBy: .equal,
toItem: toview, attribute: .top,
multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint(item: regView,
attribute: .leading,
relatedBy: .equal, toItem: toview,
attribute: .leading,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: regView, attribute: .trailing,
relatedBy: .equal,
toItem: toview,
attribute: .trailing,
multiplier: 1.0,
constant: 0).isActive = true
// NSLayoutConstraint(item: regView, attribute: NSLayoutConstraint.Attribute.height, relatedBy: .equal, toItem: toview, attribute: .height, multiplier: 1.0, constant: height).isActive = true
//regView.heightAnchor.constraint(equalToConstant: height).isActive = true
}
let label = UILabel()
label.text = text
label.font = UIFont(name: "Arial", size: 12)
label.textColor = textColor
label.numberOfLines = 3
label.lineBreakMode = .byWordWrapping
label.translatesAutoresizingMaskIntoConstraints = false
regView.addSubview(label)
NSLayoutConstraint.activate([
label.trailingAnchor.constraint(equalTo: regView.trailingAnchor),
label.leadingAnchor.constraint(equalTo: regView.leadingAnchor),
label.topAnchor.constraint(equalTo: regView.topAnchor),
label.bottomAnchor.constraint(equalTo: regView.bottomAnchor) // this is the key behind expanding
])
Timer.scheduledTimer(withTimeInterval:3, repeats: false) { (action) in
UIView.animate(withDuration: 2.8, animations: {
self.regView.removeFromSuperview()
})
}
}
Edit:
let regView = UIView()
func addDisapearingView(toview: UIView, text: String, textColor: UIColor, colorView: UIColor, alpha: CGFloat, height: CGFloat){
regView.backgroundColor = colorView
regView.alpha = alpha
toview.addSubview(regView)
regView.translatesAutoresizingMaskIntoConstraints = false
var topCon:NSLayoutConstraint!
if #available(iOS 11.0, *) {
let guide = toview.safeAreaLayoutGuide
topCon = regView.bottomAnchor.constraint(equalTo: guide.topAnchor)
topCon.isActive = true
NSLayoutConstraint.activate([
regView.trailingAnchor.constraint(equalTo: guide.trailingAnchor),
regView.leadingAnchor.constraint(equalTo: guide.leadingAnchor),
// regView.bottomAnchor.constraint(equalTo: guide.bottomAnchor).isActive = true
// regView.heightAnchor.constraint(equalToConstant: height).isActive = true
])
} else {
topCon = NSLayoutConstraint(item: regView,
attribute: .bottom,
relatedBy: .equal,
toItem: toview, attribute: .top,
multiplier: 1.0, constant: 0)
topCon.isActive = true
NSLayoutConstraint(item: regView,
attribute: .leading,
relatedBy: .equal, toItem: toview,
attribute: .leading,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: regView, attribute: .trailing,
relatedBy: .equal,
toItem: toview,
attribute: .trailing,
multiplier: 1.0,
constant: 0).isActive = true
// NSLayoutConstraint(item: regView, attribute: NSLayoutConstraint.Attribute.height, relatedBy: .equal, toItem: toview, attribute: .height, multiplier: 1.0, constant: height).isActive = true
//regView.heightAnchor.constraint(equalToConstant: height).isActive = true
}
let label = UILabel()
label.text = text
label.font = UIFont(name: "Arial", size: 12)
label.textColor = textColor
label.numberOfLines = 3
label.lineBreakMode = .byWordWrapping
label.translatesAutoresizingMaskIntoConstraints = false
regView.addSubview(label)
NSLayoutConstraint.activate([
label.trailingAnchor.constraint(equalTo: regView.trailingAnchor),
label.leadingAnchor.constraint(equalTo: regView.leadingAnchor),
label.topAnchor.constraint(equalTo: regView.topAnchor),
label.bottomAnchor.constraint(equalTo: regView.bottomAnchor) // this is the key behind expanding
])
regView.layoutIfNeeded()
topCon.constant += self.regView.frame.height
UIView.animate(withDuration: 2) {
toview.layoutIfNeeded()
}
Timer.scheduledTimer(withTimeInterval:3, repeats: false) { (action) in
UIView.animate(withDuration: 2.8, animations: {
self.regView.removeFromSuperview()
})
}
}

Adding views dynamically to UIStackview

I'm trying to add views(or buttons) to UIStackView dynamically.
At first, the UIStackView has no arranged views (vertically), and
after getting from some http response, several views(buttons) are added to UIStackView.
UIStackView is also autolayout to hold a specific area.
I've tried to find dynamic adding example, but failed.
Anyone can show me the examples of adding view onto UIStackView dynamically?
It may help you. Please follow this points:
Add UIScrollView to your UIViewController in storyboard or XIB.
Initiate an NSMutableArray name it arrViews gets server response and adds view in the array.
Initialise UIStackViewpass arrView array in the init method.
After that UIStackView will be added subview of UIScrollView.
Add constraint programmatically to UIStackView. That's it.
if let response = self.serverResponse {
if let body = response.responseBody {
if let view = body.views {
arrViews = createSubViews(view)
}
}
}
let stackView = UIStackView(arrangedSubviews: arrViews)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.spacing = 16
stackView.distribution = .fill
self.scrollView.addSubview(stackView)
//constraints
let leading = NSLayoutConstraint(item: stackView, attribute: .leading, relatedBy: .equal, toItem: self.scrollView, attribute: .leading, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(leading)
let trailing = NSLayoutConstraint(item: stackView, attribute: .trailing, relatedBy: .equal, toItem: self.scrollView, attribute: .trailing, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(trailing)
let top = NSLayoutConstraint(item: stackView, attribute: .top, relatedBy: .equal, toItem: self.scrollView, attribute: .top, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(top)
let bottom = NSLayoutConstraint(item: stackView, attribute: .bottom, relatedBy: .equal, toItem: self.scrollView, attribute: .bottom, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(bottom)
let equalWidth = NSLayoutConstraint(item: stackView, attribute: .width, relatedBy: .equal, toItem: self.scrollView, attribute: .width, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(equalWidth)
leading.isActive = true
trailing.isActive = true
top.isActive = true
bottom.isActive = true
equalWidth.isActive = true
Hope it will help you. Happy coding :)
I use this code in one of my projects:
let baseFrame = CGRect(origin: .zero, size: CGSize(width: requiredWidth, height: partitionHeight))
for instrument in instruments {
let partitionView = PartitionOnDemand(instrument: instrument, mode: playbackMode, frame: baseFrame, referenceView: partitionsAnimator)
partitionsStackView.addArrangedSubview(partitionView)
let tab = InstrumentInfoTabContainer.instantiate(with: instrument) {
self.focus(on: instrument)
}
tabsStackView.addArrangedSubview(tab)
}
While trying with answers, I happend to find how to work it.
class ViewController: UIViewController {
#IBOutlet weak var stack: UIStackView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func onBtn_Create(_ sender: Any) {
createButton("new button ...")
}
#IBAction func onBtn_Delete(_ sender: Any) {
if let v = stack.arrangedSubviews.last {
stack.removeArrangedSubview(v)
v.removeFromSuperview()
}
}
func createButton(_ title: String) {
let button = UIButton()
button.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
button.backgroundColor = UIColor.blue
button.setTitle(title, for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
stack.addArrangedSubview(button)
}
#objc func buttonAction(sender: UIButton!) {
print("Button tapped")
}
}
And, I anchored to UIStackView, Trailing=0, Leading=0, Top=0, Bottom=8 to TextView.Top
The subviews inside it are intact without any constraints.
Thank you.

How do I left align the title of a navigation bar in Xcode?

I've been trying the following in order to get the title of a navigation bar left aligned:
In the AppDelegate.swift file:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
UINavigationBar.appearance().barTintColor = UIColor.red
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName:
UIColor.white]
return true
}
In a TableViewController.swift file:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.navigationController?.navigationBar.topItem?.title = "Home"
}
but nothing I find solves the problem. I also tried the following that I found on here which does not show anything:
in the AppDelegate.swift file:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
UINavigationBar.appearance().barTintColor = UIColor.red
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName:
UIColor.white]
let lbNavTitle = UILabel (frame: CGRect(x: 0, y: 40, width: 320, height: 40))
lbNavTitle.center = CGPoint(x: 160, y: 285)
lbNavTitle.textAlignment = .left
lbNavTitle.text = "Home"
self.navigationItem.titleView = lbNavTitle
In a TableViewController.swift file:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.navigationController?.navigationBar.topItem?.title = "Home"
let lbNavTitle = UILabel (frame: CGRect(x: 0, y: 0, width: 320, height: 40))
lbNavTitle.backgroundColor = UIColor.red
lbNavTitle.textColor = UIColor.black
lbNavTitle.numberOfLines = 0
lbNavTitle.center = CGPoint(x: 0, y: 0)
lbNavTitle.textAlignment = .left
lbNavTitle.text = "Home"
let string = NSMutableAttributedString ("Title/nSubTitle")
self.navigationItem.titleView = lbNavTitle
}
let label = UILabel()
label.textColor = UIColor.white
label.text = "TCO_choose_reminder".localized;
self.navigationItem.leftBarButtonItem = UIBarButtonItem.init(customView: label)
I init a UIBarButtonItem with the constructor that gets a UIView and set my desired label as parameter to get the following.
Edit:
If you do not want the back button to be removed. Set the following flag.
self.navigationItem.leftItemsSupplementBackButton = true
Keep in mind that having a label + Back button (with title) would not look cool.In this case I replace the default back button with an arrow asset.
You can use the navigationItems titleView to add a UILabel with left alignment and then set its frame using auto layout like this:
let label = UILabel()
label.text = "Title Label"
label.textAlignment = .left
self.navigationItem.titleView = label
label.translatesAutoresizingMaskIntoConstraints = false
label.superview?.addConstraint(NSLayoutConstraint(item: label, attribute: .centerX, relatedBy: .equal, toItem: label.superview, attribute: .centerX, multiplier: 1, constant: 0))
label.superview?.addConstraint(NSLayoutConstraint(item: label, attribute: .width, relatedBy: .equal, toItem: label.superview, attribute: .width, multiplier: 1, constant: 0))
label.superview?.addConstraint(NSLayoutConstraint(item: label, attribute: .centerY, relatedBy: .equal, toItem: label.superview, attribute: .centerY, multiplier: 1, constant: 0))
label.superview?.addConstraint(NSLayoutConstraint(item: label, attribute: .height, relatedBy: .equal, toItem: label.superview, attribute: .height, multiplier: 1, constant: 0))
Well, if you'd like to use large title, then it is left aligned by defaut.
For whole navigation hierarchy.
navigationController?.navigationBar.prefersLargeTitles = true
For 1 viewController
navigationItem.largeTitleDisplayMode = .always
If u want a simple left aligned text that covers the entire titleView area heres my solution guys!.
func setLeftAlignTitleView(font: UIFont, text: String, textColor: UIColor) {
guard let navFrame = navigationController?.navigationBar.frame else{
return
}
let parentView = UIView(frame: CGRect(x: 0, y: 0, width: navFrame.width*3, height: navFrame.height))
self.navigationItem.titleView = parentView
let label = UILabel(frame: .init(x: parentView.frame.minX, y: parentView.frame.minY, width: parentView.frame.width, height: parentView.frame.height))
label.backgroundColor = .clear
label.numberOfLines = 2
label.font = font
label.textAlignment = .left
label.textColor = textColor
label.text = text
parentView.addSubview(label)
}
This works for me (iOS 13+):
let offset = UIOffset(horizontal: -CGFloat.greatestFiniteMagnitude, vertical: 0)
navigationController?.navigationBar.standardAppearance.titlePositionAdjustment = offset
navigationController?.navigationBar.scrollEdgeAppearance?.titlePositionAdjustment = offset
navigationController?.navigationBar.compactAppearance?.titlePositionAdjustment = offset