How to change the text attribute of a UILabel in Swift? - ios

I've set up a UILabel programmatically and I'm attempting to change the text attribute via a function I call later on in the ViewController however when that function is called the questionLabel.text stays the default value "Welcome".
Essentially what I'm trying to accomplish is:
func changeLabelText() {
questionLabel.text = "New label text"
print(questionLabel.text!)
}
changeLabelText()
// prints "New label text"
however what I'm actually getting is:
func changeLabelText() {
questionLabel.text = "New label text"
print(questionLabel.text!)
}
changeLabelText()
// prints "Welcome"
This is how my label is setup:
class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate {
#IBOutlet var cameraView: UIView!
var questionLabel: UILabel {
let label = UILabel()
label.lineBreakMode = .byWordWrapping
label.backgroundColor = .white
label.textColor = .black
label.text = "Welcome"
label.textAlignment = .center
label.frame = CGRect(x: 65, y: 100, width: 300, height: 65)
return label
}
Any suggestions? Greatly appreciated!

The current
var questionLabel: UILabel {
let label = UILabel()
label.lineBreakMode = .byWordWrapping
label.backgroundColor = .white
label.textColor = .black
label.text = "Welcome"
label.textAlignment = .center
label.frame = CGRect(x: 65, y: 100, width: 300, height: 65)
return label
}
is a computed property so every access gets a new separate instance
questionLabel.text = "New label text" // instance 1
print(questionLabel.text!) // instance 2
instead you need a closure
var questionLabel: UILabel = {
let label = UILabel()
label.lineBreakMode = .byWordWrapping
label.backgroundColor = .white
label.textColor = .black
label.text = "Welcome"
label.textAlignment = .center
label.frame = CGRect(x: 65, y: 100, width: 300, height: 65)
return label
}()

Change your computed variable to a lazy initializer like so:
lazy var questionLabel: UILabel = {
let label = UILabel()
label.lineBreakMode = .byWordWrapping
label.backgroundColor = .white
label.textColor = .black
label.text = "Welcome"
label.textAlignment = .center
label.frame = CGRect(x: 65, y: 100, width: 300, height: 65)
return label
}()

Klamont,
You can try this.
Suppose you want to change the some text of your label you always create two labels for that but it's a wrong approach of changing text color of label. You can use the NSMutableAttributedString for changing the some text color of your label.Firstly, you have to find the the range of text, which you want to change the color of that text and then set the range of your text to the NSMutableAttributedString object as compared to full string and then set your label attributedText with the NSMutableAttributedString object.
Example:
let strNumber: NSString = "Hello Test" as NSString // you must set your
let range = (strNumber).range(of: "Test")
let attribute = NSMutableAttributedString.init(string: strNumber)
attribute.addAttribute(NSForegroundColorAttributeName, value: UIColor.red , range: range)
yourLabel.attributedText = attribute
If you want to use this in many times in your application you can just create the extension of the UILabel and it will make more simple :-
extension UILabel {
func halfTextColorChange (fullText : String , changeText : String ) {
let strNumber: NSString = fullText as NSString
let range = (strNumber).range(of: changeText)
let attribute = NSMutableAttributedString.init(string: fullText)
attribute.addAttribute(NSForegroundColorAttributeName, value: UIColor.red , range: range)
self.attributedText = attribute
}
}
Use your label:-
yourLabel = "Hello Test"
yourLabel.halfTextColorChange(fullText: totalLabel.text!, changeText: "Test")

Related

Add UILabel on random position on UIView

How do I Achieve Something like below image,
I want to add n number of UILabel to screen on random position without overlapping of label, and also randomly set rotate.
Here is my code, but it is overlapping label to another label:
override func viewDidLoad() {
super.viewDidLoad()
var textDatas = ["aaa","bbb","cccccc","ddddddd","ee","ffffff","ggggg","hhhh","iiiiii","jjjjjj","kkkkkk","ll","mmmmm","nnnnnnnn","ooooooooo","ppppppppp","qqqqqqqqqqqq","rrrrrrrr","sssssss","ttttttttt","uuuuuuu","vvvv","wwww","xxxxxx","yyyy","zzzzzzz"]
textDatas.forEach { (textData) in
let randomIntX = Int.random(in: 10..<Int(ScreenSize.SCREEN_WIDTH-200))
let randomIntY = Int.random(in: 50..<Int(ScreenSize.SCREEN_HEIGHT-50))
let randomIntFontSize = Int.random(in: 10..<40)
let randomIntLabelRotate = Int.random(in: 1..<3)
let label = UILabel(frame: CGRect(x: randomIntX, y: randomIntY, width: 200, height: 30))
label.font = UIFont(name: FONT_AVENIR_HEAVY,
size: CGFloat(randomIntFontSize))
label.textColor = .white
label.textAlignment = .center
label.text = textData
label.sizeToFit()
if randomIntLabelRotate == 1 {
label.transform = CGAffineTransform(rotationAngle: -CGFloat.pi / 2)
}
self.view.addSubview(label)
}
}
Is there any library or solution of this will be very appreciated. Thanks

How to check current font size of UILabel?

I have 3 UILabels and I want to set same (the smallest one from all of 3 possibilities) font for all of them. What is the problem? I don't know how to check the current font - each of UILabels use Autoshrink with minimal font scale and lines amount equaled to 0. The text of UILabels is set in ViewDidLoad() method (there are many combinations of possible label texts).
I tried to get the current font size with UILabel.font.pointSize property (called in viewDidAppear() method) and than compare all of them. The problem is that that UILabel.font.pointSize returns not current value of UILabel text font size (after Autoshrink has been done) but the value that is set in storyboard.
I'm totally out of ideas so thanks for the help!
Greetings, John
use this extention
extension String {
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
return ceil(boundingBox.height)
}
}
get height
height = strDesc.height(withConstrainedWidth: UIScreen.main.bounds.size.width - 160, font: UIFont.systemFont(ofSize: 14.0))
This is one way to get the current size of the UILabel pointsize although I know of more ways if this does not work. This is the cleanest and fastest. See example and extension included.
import UIKit
class ViewController: UIViewController {
lazy var label1 : UILabel = {
let lbl = UILabel(frame: CGRect(x: 20, y: 80, width: self.view.bounds.width - 40, height: 40))
lbl.text = "This is some large text to make it fit to size with a big font size"
lbl.font = UIFont.systemFont(ofSize: 50, weight: .bold)
lbl.textColor = .black
lbl.minimumScaleFactor = 0.01
lbl.adjustsFontSizeToFitWidth = true
lbl.numberOfLines = 0
return lbl
}()
lazy var label2 : UILabel = {
let lbl = UILabel(frame: CGRect(x: 20, y: label1.frame.maxY + 5, width: self.view.bounds.width - 40, height: 40))
lbl.text = "This is one line"
lbl.font = UIFont.systemFont(ofSize: 20, weight: .bold)
lbl.textColor = .black
lbl.minimumScaleFactor = 0.01
lbl.adjustsFontSizeToFitWidth = true
lbl.numberOfLines = 0
return lbl
}()
lazy var label3 : UILabel = {
let lbl = UILabel(frame: CGRect(x: 20, y: label2.frame.maxY + 10, width: self.view.bounds.width - 40, height: 80))
lbl.text = "This is some large text to make it fit to size with a big font size"
lbl.font = UIFont.systemFont(ofSize: 100, weight: .bold)
lbl.textColor = .black
lbl.minimumScaleFactor = 0.01
lbl.adjustsFontSizeToFitWidth = true
lbl.numberOfLines = 0
return lbl
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.view.addSubview(label1)
self.view.addSubview(label2)
self.view.addSubview(label3)
var minSize : CGFloat = .greatestFiniteMagnitude
for sub in self.view.subviews{
if let lbl = sub as? UILabel{
//get the size
let font = lbl.adjustedFont()
print("the size is \(font.pointSize)")
minSize = min(font.pointSize,minSize)
}
}
print("the minimum for all fonts is \(minSize)")
print("i am going to delay for 3 seconds and then reset all the labels to the minimum size :)")
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3.0) {
for sub in self.view.subviews{
if let lbl = sub as? UILabel{
lbl.font = lbl.font.withSize(minSize)
}
}
}
}
}
extension UILabel{
func adjustedFont()->UIFont {
guard let txt = text else {
return self.font
}
let attributes: [NSAttributedString.Key: Any] = [.font: self.font]
let attributedString = NSAttributedString(string: txt, attributes: attributes)
let drawingContext = NSStringDrawingContext()
drawingContext.minimumScaleFactor = self.minimumScaleFactor
attributedString.boundingRect(with: bounds.size,
options: [.usesLineFragmentOrigin,.usesFontLeading],
context: drawingContext)
let fontSize = font.pointSize * drawingContext.actualScaleFactor
return font.withSize(CGFloat(floor(Double(fontSize))))
}
}
Example in Action
What I've found is that inside viewDidLoad() your constraints are not fully defined yet. You can try to do these calculations in another method for example viewWillAppear() or any other that suits you and is executed after viewDidLoad().

Not seeing all views in views using views.addSubviews

I am running into a basic problem with views on iPad. I have a viewController. The view is using a UIImageView with a full screen image as a background image. I am trying to overlay labels on the top. If I set labels individually, both views show up. If I call a function with the same information only one view shows up. I need to extend it many labels. Here is the code:
class ViewController: UIViewController {
#IBOutlet weak var backGroundImageView: UIImageView!
var cFrame:[CGRect?] = [CGRect?](repeating: nil, count: 13)
var offsets:[CGRect?] = [CGRect?](repeating: nil, count: 13)
var labels: [UILabel?] = [UILabel?](repeating:UILabel(), count:13)
override func viewDidLoad() {
super.viewDidLoad()
cFrame[0] = CGRect(x:450,y:530,width:251,height:68)
cFrame[1] = CGRect(x:147,y:676,width:222,height:24)
loadFrameValues()
var frame = CGRect(x: 450, y: 520, width: 251, height: 68)
let label0 = UILabel(frame: frame)
label0.backgroundColor = .white
label0.numberOfLines = 0
label0.lineBreakMode = .byWordWrapping
label0.textAlignment = .center
label0.text = "Text 1"
frame = CGRect(x: 152, y: 686, width: 210, height: 16)
let label1 = UILabel(frame: frame)
label1.backgroundColor = .cyan
label1.lineBreakMode = .byWordWrapping
label1.textAlignment = .left
label1.text = "Text 2"
label1.font = label1.font.withSize(12)
backGroundImageView.addSubview(label0)
backGroundImageView.addSubview(label1)
// showView(label: labels[0]!, frame: cFrame[0]!)
// showView(label: labels[1]!, frame: cFrame[1]!)
// }
}
func showView(label: UILabel, frame:CGRect) {
label.frame = frame
label.backgroundColor = .white
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.textAlignment = .center
label.text = "Syed Tariq"
backGroundImageView.addSubview(label)
}
You can't add subview (label in your case) to UIImageView. So add label as subview to the superview of your image view.
override func viewDidLoad() {
cFrame[0] = CGRect(x:50,y:130,width:151,height:68)
cFrame[1] = CGRect(x:114,y:276,width:122,height:24)
showView(frame: cFrame[0]!)
showView(frame: cFrame[1]!)
}
func showView(frame:CGRect) {
let label = UILabel(frame: frame)
label.backgroundColor = .green
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.textAlignment = .center
label.text = "Syed Tariq"
self.view.addSubview(label)
}

Multiline Navigationbar Title

I am trying to set the title label in my navigation bar to allow multiple lines. I have custom navigation controller code that I am placing the multiline code into. I know that the code already there works, but my multiline part is not working.
let titleLabel = UILabel()
titleLabel.frame = CGRectMake(0, 0, self.navigationBar.frame.width, self.navigationBar.frame.height * 2)
titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .ByWordWrapping
navigationItem.titleView = titleLabel
But the text still runs off at the end. I've also tried putting this into the individual view controller itself, adding self.navigationController?. in front of navigationItem with the same results.
Is there something I'm missing in my code that would keep the title label from using multiple lines?
Here is a code example of how you can create a multiline navigationBar title
let label: UILabel = UILabel(frame: CGRectMake(0, 0, 400, 50))
label.backgroundColor = UIColor.clearColor()
label.numberOfLines = 2
label.font = UIFont.boldSystemFontOfSize(16.0)
label.textAlignment = .Center
label.textColor = UIColor.whiteColor()
label.text = "This is a\nmultiline string for the navBar"
self.navigationItem.titleView = label
Swift 5.x:
let label = UILabel()
label.backgroundColor = .clear
label.numberOfLines = 2
label.font = UIFont.boldSystemFont(ofSize: 16.0)
label.textAlignment = .center
label.textColor = .white
label.text = "This is a\nmultiline string for the navBar"
self.navigationItem.titleView = label
This is doable in a storyboard. Just drag a UIView into the Navigation bar, then drag a UILabel onto it in the document outline, set lines to 2 and alignment to center.
Use this to get the label position exactly as you want it:
let labelWidth = navBar.bounds.width - 110
let label = UILabel(frame: CGRect(x:(navBar.bounds.width/2) - (labelWidth/2), y:0, width:labelWidth, height:navBar.bounds.height))
label.backgroundColor = UIColor.clear
label.numberOfLines = 0
label.font = UIFont.boldSystemFont(ofSize: 13.0)
label.textAlignment = .center
label.textColor = UIColor.black
label.lineBreakMode = .byWordWrapping
label.text = loadedName
navBar.topItem?.title = nil
navBar.addSubview(label)
the 110 value in the top line is the spacing you want either side of the label.
swift 5+ very easy solution
func titleMultiLine(topText: String, bottomText: String) {
// let titleParameters = [NSForegroundColorAttributeName : UIColor.white,
// NSFontAttributeName : UIFont.<Font>]
// let subtitleParameters = [NSForegroundColorAttributeName : UIColor.<Color>(),
// NSFontAttributeName : UIFont.<Font>]
let titleParameters = [NSAttributedString.Key.foregroundColor : UIColor.white]
let subtitleParameters = [NSAttributedString.Key.foregroundColor : UIColor.white]
let title:NSMutableAttributedString = NSMutableAttributedString(string: topText, attributes: titleParameters)
let subtitle:NSAttributedString = NSAttributedString(string: bottomText, attributes: subtitleParameters)
title.append(NSAttributedString(string: "\n"))
title.append(subtitle)
let size = title.size()
let titleLabel = UILabel(frame: CGRect(x: 0, y: 0, width: size.width, height: size.height))
titleLabel.attributedText = title
titleLabel.numberOfLines = 0
titleLabel.textAlignment = .center
navigationItem.titleView = titleLabel
}
Function Calling
self.titleMultiLine(topText: "I am top text Title", bottomText: "bottom text")

How to create UILabel programmatically using Swift?

How do I create a UILabel programmatically using Swift in Xcode 6?
I have started with a new "Single View Application" in Xcode 6 and selected Swift for this project. I have my files AppDelegate.swift and ViewController.swift and I'm not sure what to do from here.
Creating a UILabel programmatically in Swift 3+:
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 21))
label.center = CGPoint(x: 160, y: 285)
label.textAlignment = .center
label.text = "I'm a test label"
self.view.addSubview(label)
}
Here is the correct code for Swift 3, with comments for instructional purposes:
override func viewDidLoad()
{
super.viewDidLoad()
// CGRectMake has been deprecated - and should be let, not var
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 21))
// you will probably want to set the font (remember to use Dynamic Type!)
label.font = UIFont.preferredFont(forTextStyle: .footnote)
// and set the text color too - remember good contrast
label.textColor = .black
// may not be necessary (e.g., if the width & height match the superview)
// if you do need to center, CGPointMake has been deprecated, so use this
label.center = CGPoint(x: 160, y: 284)
// this changed in Swift 3 (much better, no?)
label.textAlignment = .center
label.text = "I am a test label"
self.view.addSubview(label)
}
Just to add onto the already great answers, you might want to add multiple labels in your project so doing all of this (setting size, style etc) will be a pain. To solve this, you can create a separate UILabel class.
import UIKit
class MyLabel: UILabel {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initializeLabel()
}
override init(frame: CGRect) {
super.init(frame: frame)
initializeLabel()
}
func initializeLabel() {
self.textAlignment = .left
self.font = UIFont(name: "Halvetica", size: 17)
self.textColor = UIColor.white
}
}
To use it, do the following
import UIKit
class ViewController: UIViewController {
var myLabel: MyLabel()
override func viewDidLoad() {
super.viewDidLoad()
myLabel = MyLabel(frame: CGRect(x: self.view.frame.size.width / 2, y: self.view.frame.size.height / 2, width: 100, height: 20))
self.view.addSubView(myLabel)
}
}
Swift 4.X and Xcode 10
let lbl = UILabel(frame: CGRect(x: 10, y: 50, width: 230, height: 21))
lbl.textAlignment = .center //For center alignment
lbl.text = "This is my label fdsjhfg sjdg dfgdfgdfjgdjfhg jdfjgdfgdf end..."
lbl.textColor = .white
lbl.backgroundColor = .lightGray//If required
lbl.font = UIFont.systemFont(ofSize: 17)
//To display multiple lines in label
lbl.numberOfLines = 0 //If you want to display only 2 lines replace 0(Zero) with 2.
lbl.lineBreakMode = .byWordWrapping //Word Wrap
// OR
lbl.lineBreakMode = .byCharWrapping //Charactor Wrap
lbl.sizeToFit()//If required
yourView.addSubview(lbl)
If you have multiple labels in your class use extension to add properties.
//Label 1
let lbl1 = UILabel(frame: CGRect(x: 10, y: 50, width: 230, height: 21))
lbl1.text = "This is my label fdsjhfg sjdg dfgdfgdfjgdjfhg jdfjgdfgdf end..."
lbl1.myLabel()//Call this function from extension to all your labels
view.addSubview(lbl1)
//Label 2
let lbl2 = UILabel(frame: CGRect(x: 10, y: 150, width: 230, height: 21))
lbl2.text = "This is my label fdsjhfg sjdg dfgdfgdfjgdjfhg jdfjgdfgdf end..."
lbl2.myLabel()//Call this function from extension to all your labels
view.addSubview(lbl2)
extension UILabel {
func myLabel() {
textAlignment = .center
textColor = .white
backgroundColor = .lightGray
font = UIFont.systemFont(ofSize: 17)
numberOfLines = 0
lineBreakMode = .byCharWrapping
sizeToFit()
}
}
Create UILabel view outside viewDidLoad class and then add that view to your main view in viewDidLoad method.
lazy var myLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "This is label view."
label.font = UIFont.systemFont(ofSize: 12)
return label
}()
And then add that view in viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(myLabel)
// Set its constraint to display it on screen
myLabel.widthAnchor.constraint(equalToConstant: 200).isActive = true
myLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
You can create a label using the code below. Updated.
let yourLabel: UILabel = UILabel()
yourLabel.frame = CGRect(x: 50, y: 150, width: 200, height: 21)
yourLabel.backgroundColor = UIColor.orange
yourLabel.textColor = UIColor.black
yourLabel.textAlignment = NSTextAlignment.center
yourLabel.text = "test label"
self.view.addSubview(yourLabel)
Another answer in Swift 3:
let myLabel = UILabel()
myLabel.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
myLabel.center = CGPoint(x: 0, y: 0)
myLabel.textAlignment = .center
myLabel.text = "myLabel!!!!!"
self.view.addSubview(myLabel)
Create label in swift 4
let label = UILabel(frame: CGRect(x: self.view.frame.origin.x, y: self.view.frame.origin.y, width: self.view.frame.size.width, height: 50))
label.textAlignment = .center
label.text = "Hello this my label"
//To set the color
label.backgroundColor = UIColor.white
label.textColor = UIColor.black
//To set the font Dynamic
label.font = UIFont(name: "Helvetica-Regular", size: 20.0)
//To set the system font
label.font = UIFont.systemFont(ofSize: 20.0)
//To display multiple lines
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping //Wrap the word of label
label.lineBreakMode = .byCharWrapping //Wrap the charactor of label
label.sizeToFit()
self.view.addSubview(label)
An alternative using a closure to separate out the code into something a bit neater using Swift 4:
class theViewController: UIViewController {
/** Create the UILabel */
var theLabel: UILabel = {
let label = UILabel()
label.lineBreakMode = .byWordWrapping
label.textColor = UIColor.white
label.textAlignment = .left
label.numberOfLines = 3
label.font = UIFont(name: "Helvetica-Bold", size: 22)
return label
}()
override func viewDidLoad() {
/** Add theLabel to the ViewControllers view */
view.addSubview(theLabel)
}
override func viewDidLayoutSubviews() {
/* Set the frame when the layout is changed */
theLabel.frame = CGRect(x: 0,
y: 0,
width: view.frame.width - 30,
height: 24)
}
}
As a note, attributes for theLabel can still be changed whenever using functions in the VC. You're just setting various defaults inside the closure and minimizing clutter in functions like viewDidLoad()
Swift 4.2 and Xcode 10. Somewhere in ViewController:
private lazy var debugInfoLabel: UILabel = {
let label = UILabel()
label.textColor = .white
label.translatesAutoresizingMaskIntoConstraints = false
yourView.addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: suggestionView.centerXAnchor),
label.centerYAnchor.constraint(equalTo: suggestionView.centerYAnchor, constant: -100),
label.widthAnchor.constraint(equalToConstant: 120),
label.heightAnchor.constraint(equalToConstant: 50)])
return label
}()
...
Using:
debugInfoLabel.text = debugInfo
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 21))
label.center = CGPoint(x: 160, y: 285)
label.textAlignment = .center
label.text = "My label"
self.view.addSubview(label)
Try above code in ViewDidLoad
Swift 4.2 and Xcode 10
Initialize label before viewDidLoad.
lazy var topLeftLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "TopLeft"
return label
}()
In viewDidLoad add label to the view and apply constraints.
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(topLeftLabel)
topLeftLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
topLeftLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10).isActive = true
}

Resources