Label does not adjust Font Size to fit width - swift

This is how my app looks although I entered this code inside my ViewController class:
#IBOutlet var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label.text = "Hello World"
label.adjustsFontSizeToFitWidth = true
label.numberOfLines = 1
label.minimumScaleFactor = 0.1
}

Your text data is not more than label width that's why label text font is same as already set.
IF your text data is more then label width then it will adjust font according to the width.
Please check with label text: "This is the demo to test label text is adjustable or not. You need to test it with this demo data"
Your label font will adjust according to the width.

The font will adjust if the given text is greater than the width of the label.
Try this in playground:
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let label = UILabel()
label.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
label.adjustsFontSizeToFitWidth = true
label.numberOfLines = 1
label.backgroundColor = UIColor.lightGray
label.text = "Hello World! How are you doing today? "
label.textColor = .black
view.addSubview(label)
self.view = view
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
The result is the following:

I was able to get my UILabel font to dynamically adjust to the necessary size to fit into its parent by following this simple gitconnected article (See link to get all required code!!). I only needed to make two adjustments which were adding the lines label.baselineAdjustment = .alignCenters and label.numberOfLines = 1 so that my label creation now looked like this...
let dynamicFontLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 40)
label.textAlignment = .center
label.numberOfLines = 1;
label.textColor = .black
label.adjustsFontSizeToFitWidth = true
label.baselineAdjustment = .alignCenters
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
The label.baselineAdjustment = .alignCenters property ensured that if my font size was too large and needed to be downsized, my text would still remain centered vertically in the UILabel. I also only wanted my text to only span one line so if you want more than that you can just remove the label.numberOfLines = 1 property.

Related

Swift UIKIT components do not show on a working view

I'm turning my hand from years of Java / C++ / C to trying to learn Swift and how to develop on Apple.
I've tried a lot of tutorials and for what I want to achieve I need to use a splitview controller.
I need to have a few different detail views and can't work out how to do this via storyboard and thought I'd try to do it with code.
Following a tutorial and several google searches Im hitting a problem which I realise is me being a newbiee and ask for your help.
To keep things simple I created a test viewcontroller class and set it in the storyboard no problem.
import UIKit;
class tesctVC : UIViewController {
var scoreLabel: UILabel!
override func loadView() {
view = UIView()
view.backgroundColor = .white
scoreLabel = UILabel()
scoreLabel.translatesAutoresizingMaskIntoConstraints = false
scoreLabel.textAlignment = .right
scoreLabel.text = "Score: 0"
view.addSubview(scoreLabel)
// more code to come!
}
}
When I change to background color it shows as expected.
But when I try to add anything else, I've tried UILabels, UIButtons etc, they do not show.
Would you please give me some pointers?
Thanks again.
I thought this may help others.
This is still WIP but shows some hints I found along the way.
I want to add a bunch of labels & buttons etc.
This in so far as is written does the job. It needs more formatting and so on.
The extension is needed to set a stacks background color. I think someone explained its because a StackView is not a drawable object.
extension UIStackView {
func addBackground(color: UIColor) {
let subView = UIView(frame: bounds)
subView.backgroundColor = color
subView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
insertSubview(subView, at: 0)
}
}
class DetailViewController: UIViewController , UIWebViewDelegate {
var usernameEdt: UITextField!
var passwordEdt: UITextField!
var statusLabel: UILabel!
var LogubButton = UIButton()
// Draw the login page
func loginPage() {
// Create the top level statck
let stackView = UIStackView()
stackView.addBackground(color: .white)
stackView.axis = .vertical
stackView.alignment = .center // .leading .firstBaseline .center .trailing .lastBaseline
stackView.distribution = .fillEqually // .fillEqually .fillProportionally .equalSpacing .equalCentering
// Add the title
let label = UILabel()
label.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
label.font = UIFont.boldSystemFont(ofSize: 20.0)
label.text = "Login Details"
label.textColor = .blue
stackView.addArrangedSubview(label)
// Add Username
usernameEdt = UITextField()
usernameEdt.placeholder = "Enter Your Username"
stackView.addArrangedSubview(usernameEdt)
// Add Password
passwordEdt = UITextField()
passwordEdt.placeholder = "Enter Your Password"
stackView.addArrangedSubview(passwordEdt)
self.view = stackView
}
Thanks
Jeff

How to add a subView to all UITable Cells

Without using storyboard.
I'm trying to add an error label to any cell where a value is not filled out/saved. I concluded that I dont need to show this logic and the issue is showing more than one error labels in all/more than one tableView's cell.
I've created this viewLabel to reuse:
struct Label {
static let errorLabel: UILabel = {
let label = UILabel()
label.frame = CGRect(x: 0, y: 0, width: 18, height: 18)
label.text = "!"
label.layer.cornerRadius = label.frame.height / 2
label.backgroundColor = UIColor.red
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .center
label.textColor = UIColor.white
label.font = UIFont(name: "CircularStd-Black", size: 14)
label.clipsToBounds = true
return label
}()
}
Inside cellForRowAt:
// I'm using detailTextLabel
let cell = UITableViewCell(style: .value1, reuseIdentifier: cellId)
cell.addSubview(Label.errorLabel)
// [...] constraints for Label.errorLabel
return cell
Based on this example, I'd expect to show a red circle on all cells but instead, it shows on the last cell. Why?
A few things wrong here:
You should only add to cell contentView. (https://developer.apple.com/documentation/uikit/uitableviewcell/1623229-contentview)
Example:
cell.contentView.addSubview(myLabel)
Better reuse would be to add your, label once.
This can be done in interface builder or init or awakeFromNib. This way reuse will be more efficient.
And this is the main issue you are seeing:
You are adding one static label, again and again.
Meaning: only the last cell will display it because there is only one label (:
Better use a function to create the label (Factory function)
static func createLabel() -> UILabel {
let label = UILabel()
label.frame = CGRect(x: 0, y: 0, width: 18, height: 18)
label.text = "!"
label.layer.cornerRadius = label.frame.height / 2
label.backgroundColor = UIColor.red
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .center
label.textColor = UIColor.white
label.font = UIFont(name: "CircularStd-Black", size: 14)
label.clipsToBounds = true
return label
}

UIView Mask Is Causing View To Move Positions (AutoLayout)

In my app, I have a UILabel, which I am using to mask a UIView. I am using AutoLayout throughout the app, and am finding that when setting the mask of my label, its position suddenly changes.
Here is my code when adding my label;
// Label
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Hello!"
label.font = UIFont.systemFont(ofSize: 50.0)
label.textColor = UIColor.white
view.addSubview(label)
// Constraints
label.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
This produces the result. When adding the mask, however, via the following code;
// Mask
let mask = UIView()
mask.translatesAutoresizingMaskIntoConstraints = false
mask.backgroundColor = UIColor.blue
mask.mask = label
view.addSubview(mask)
// Constraints
mask.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
mask.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
mask.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
mask.heightAnchor.constraint(equalTo: self.view.heightAnchor).isActive = true
My label ends up repositioning itself, and I am seeking to have the text stay in position at the perfect center.
You cannot use Auto Layout on the view that is used as a mask. That view it lives outside the normal view hierarchy. You do not add it to the view hierarchy by calling addSubview(_:), you only add it as a mask by setting it as the mask property of another view.
Because of that you have to set the label's frame directly to center your label. You also have to set it again everytime the frame of your masked view changed (e.g. if the user rotates the device). Because of that you have to set the label's frame in viewDidLayoutSubviews()
I tried to make it work by just setting the label's center to the view's center, but that does not work. Somehow the label does not get displayed. I could make it work by explicitly setting the labels size to its intrinsicContentSize. I guess this is because the label is used as a masked and never part of the view hierarchy.
Here is a working example. I took the liberty to change the naming from mask to maskedView to avoid confusion with the mask property ;-)
class ViewController: UIViewController {
var label = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
// Label
label.text = "Hello!"
label.font = UIFont.systemFont(ofSize: 50.0)
label.textColor = UIColor.white
// Mask
let maskedView = UIView()
maskedView.translatesAutoresizingMaskIntoConstraints = false
maskedView.backgroundColor = UIColor.blue
maskedView.mask = label
view.addSubview(maskedView)
// Constraints
maskedView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
maskedView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
maskedView.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
maskedView.heightAnchor.constraint(equalTo: self.view.heightAnchor).isActive = true
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let labelHeight = label.intrinsicContentSize.height
let labelWidth = label.intrinsicContentSize.width
label.frame = CGRect(
x: view.center.x - labelWidth / 2,
y: view.center.y - labelHeight / 2,
width: labelWidth,
height: labelHeight
)
}
}
You could make the code inside viewDidLayoutSubviews() a bit shorter by setting label.textAlignment = .center
Then this is enough:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
label.frame = view.frame
}

Swift font.withSize not changing font size on UILabel

I was having trouble changing font size in my project, so I made a playground. No matter where I put the font.withSize property, the simulator does not reflect the font size change.
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let label = UILabel()
label.font.withSize(80)
label.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
label.text = "Hello Mom!"
label.textColor = .black
view.addSubview(label)
self.view = view
}
}
withSize(_:) does not modify the font. It returns a new font with the same properties as the font you called it on, but with the new size. You have to assign the label's font to it instead:
label.font = label.font.withSize(80)
You cannot set the size of a UIFont.
UIFont.withSize() is an initializer and will return a new UIFont object with the same characteristics as the original but with the font size specified.
So you need to assign a new font with the correct size to your label.
For your example, you could use:
label.font = label.font.withSize(80)

How to resize Title in a navigation bar dynamically

I have some views that show up in a navigation controller. Two of these views have a longer title for the navigation bar.
The problem is that when the title is too long to fit, some characters are truncated and "..." is added.
Is there any way I can tell the Navigation bar to re-size the title text automatically to fit?
Used the below code in ViewDidload .
Objective C
self.title = #"Your TiTle Text";
UILabel* tlabel=[[UILabel alloc] initWithFrame:CGRectMake(0,0, 200, 40)];
tlabel.text=self.navigationItem.title;
tlabel.textColor=[UIColor whiteColor];
tlabel.font = [UIFont fontWithName:#"Helvetica-Bold" size: 30.0];
tlabel.backgroundColor =[UIColor clearColor];
tlabel.adjustsFontSizeToFitWidth=YES;
tlabel.textAlignment = NSTextAlignmentCenter;
self.navigationItem.titleView=tlabel;
Swift Version
self.title = "Your Title Text"
let tlabel = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 40))
tlabel.text = self.title
tlabel.textColor = UIColor.white
tlabel.font = UIFont.systemFont(ofSize: 30, weight: .bold)
tlabel.backgroundColor = UIColor.clear
tlabel.adjustsFontSizeToFitWidth = true
tlabel.textAlignment = .center
self.navigationItem.titleView = tlabel
Hope it works for you.Thanks
Swift version of Accepted Answer + putting the label text on center :
Swift 2.3:
self.title = "Your TiTle Text"
let tlabel = UILabel(frame: CGRectMake(0, 0, 200, 40))
tlabel.text = self.title
tlabel.textColor = UIColor.whiteColor()
tlabel.font = UIFont.boldSystemFontOfSize(17) //UIFont(name: "Helvetica", size: 17.0)
tlabel.backgroundColor = UIColor.clearColor()
tlabel.adjustsFontSizeToFitWidth = true
tlabel.textAlignment = .Center
self.navigationItem.titleView = tlabel
And Swift 3 :
self.title = "Your TiTle Text"
let frame = CGRect(x: 0, y: 0, width: 200, height: 40)
let tlabel = UILabel(frame: frame)
tlabel.text = self.title
tlabel.textColor = UIColor.white
tlabel.font = UIFont.boldSystemFont(ofSize: 17) //UIFont(name: "Helvetica", size: 17.0)
tlabel.backgroundColor = UIColor.clear
tlabel.adjustsFontSizeToFitWidth = true
tlabel.textAlignment = .center
self.navigationItem.titleView = tlabel
This works for me
Objective C
[UILabel appearanceWhenContainedInInstancesOfClasses:#[[UINavigationBar class]]].adjustsFontSizeToFitWidth = YES;
Swift Version
UILabel.appearance(whenContainedInInstancesOf: [UINavigationBar.self]).adjustsFontSizeToFitWidth = true
In case you have a view added into titleView, and you want to resize the view, you can use this code (Swift 3):
self.translatesAutoresizingMaskIntoConstraints = false
self.layoutIfNeeded()
self.sizeToFit()
self.translatesAutoresizingMaskIntoConstraints = true
None of the above solutions seam to work reliably for me.
However I found a solution by using different elements of the provides answers, its in Swift 2 and is really elegant as it does not require any custom code each time you change the label, it just uses property observers on the title.
Note that in my case, I had a back button on the left side of the navigation bar, which putted the text out of the center of the screen, to fix this I am using attributed text and the tailIndent. All comments/info in the code below :
class VCHowToTopic : UIViewController {
//add handlers so that any manipulation of the title is caught and transferred to the custom drawn UILabel
override var title : String? {
set {
super.title = newValue
configureTitleView()
}
get {
return super.title
}
}
//MARK: - lifecycle
func configureTitleView() {
//some large number that makes the navigationbar schrink down our view when added
let someVeryLargeNumber = CGFloat(4096)
//create our label
let titleLabel = UILabel(frame: CGRect(x: 0, y: 0, width: someVeryLargeNumber, height: someVeryLargeNumber))
//0 means unlimited number of lines
titleLabel.numberOfLines = 0
//define style of the text (we will be using attributed text)
let style = NSMutableParagraphStyle()
style.alignment = .Center
//top compensate for the backbutton which moves the centered text to the right side of the screen
//we introduce a negative tail indent, the number of 56 has been experimentally defined and might
//depend on the size of your custom back button (if you have one), mine is 22x22 px
style.tailIndent = -56
//create attributed text also with the right color
let attrText = NSAttributedString(string: title!, attributes: [NSParagraphStyleAttributeName : style,
NSForegroundColorAttributeName : UIColor.whiteColor()])
//configure the label to use the attributed text
titleLabel.attributedText = attrText
//add it as the titleview
navigationItem.titleView = titleLabel
}
}
You can create a UILabel as UINavigationItem's titleView and set it's adjustsFontSizeToFitWidth to true.
class MyViewController: UIViewController {
override var title: String? {
didSet {
(self.navigationItem.titleView as? UILabel)?.text = self.title
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.titleView = UILabel().apply {
$0.font = .boldSystemFont(ofSize: 18)
$0.minimumScaleFactor = 0.5
$0.adjustsFontSizeToFitWidth = true
$0.text = self.title
}
}
}
Easy to use:
myViewController.title = "This is a long title, but don’t worry."
The apply closure in the above code is a trick, in order to make the programming experience better. There is also a with closure. Recommend to everyone.
protocol ScopeFunc {}
extension ScopeFunc {
#inline(__always) func apply(_ block: (Self) -> ()) -> Self {
block(self)
return self
}
#inline(__always) func with<R>(_ block: (Self) -> R) -> R {
return block(self)
}
}
extension NSObject: ScopeFunc {}
Swift 5 and iOS 13 / iOS 14
The answers from above don't work if you have a large title in Swift 5 and iOS 13 because they simply add another title to your navigation bar. Instead you could use the largeTitleTextAttributes property (available since iOS 11) to shrink your title when needed.
Assuming you have set your large title via storyboard or code already, you can use the following method:
private func configureNavigationTitle(_ title: String) {
let tempLabel = UILabel()
tempLabel.font = UIFont.systemFont(ofSize: 34, weight: .bold)
tempLabel.text = title
if tempLabel.intrinsicContentSize.width > UIScreen.main.bounds.width - 30 {
var currentTextSize: CGFloat = 34
for _ in 1 ... 34 {
currentTextSize -= 1
tempLabel.font = UIFont.systemFont(ofSize: currentTextSize, weight: .bold)
if tempLabel.intrinsicContentSize.width < UIScreen.main.bounds.width - 30 {
break
}
}
navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedString.Key.font : UIFont.systemFont(ofSize: currentTextSize, weight: .bold)]
}
self.title = title
}
So essentially we are ussing a helper label in order to get the width of our title and then we are going to shrink the font size until the title fits in our navigation bar.
Call it from viewDidLoad():
override func viewDidLoad() {
super.viewDidLoad(
configureNavigationTitle("A very long title which fits perfectly fine")
}
you need to customize the navigation bar title view with uilabel and provide adjust font size..
[self.navigationItem setTitleView:<"Include any UI View subclass">];
Just calling sizeToFit() on my view after the change worked for me
Here's an example in Swift that also allows for multiple lines. Using PureLayout to simplify auto layout.
override func viewDidLoad() {
super.viewDidLoad()
configureTitleView()
}
func configureTitleView() {
let titleLabel = UILabel()
titleLabel.numberOfLines = 0
titleLabel.textAlignment = .Center
titleLabel.font = UIFont.boldSystemFontOfSize(17.0)
titleLabel.text = searchLoc.mapItem.name
navigationItem.titleView = titleLabel
titleLabel.autoPinEdgesToSuperviewMargins() // PureLayout method
titleLabel.adjustsFontSizeToFitWidth = true
}
And a usage example:
Swift 4 and iOS 13
Adding this so my future self can find it. Views added to titleView for some reason don't like to automatically resize themselves. So you have to do it manually.
Example
(navigationItem.titleView as? UILabel)?.text = "A longer string..." // label not resized and text is cut off
Solution
navigationItem.titleView?.translatesAutoresizingMaskIntoConstraints = false
navigationItem.titleView?.setNeedsLayout()
navigationItem.titleView?.layoutIfNeeded()
navigationItem.titleView?.translatesAutoresizingMaskIntoConstraints = true
Thanks to #Paolo Musolino for leading me here.