How to create a limit for the UILabel in swift - ios

I want to explain my question with codes. First you can see the custom label codes in the below;
let resultLabel : UILabel = {
let lbl = UILabel()
lbl.textColor = .white
lbl.textAlignment = .right
lbl.text = "0"
lbl.font = UIFont(name: "Helvetica Neue", size: 35)
lbl.minimumScaleFactor = 0.75
lbl.lineBreakMode = .byClipping
lbl.adjustsFontSizeToFitWidth = true
return lbl
}()
I just created label with these lines of codes. I also give them specific height according to view's frame height size and padding left and the right you'll see the constraint codes in the below.
resultLabel.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: view.frame.size.height / 7, paddingLeft: 2, paddingBottom: 0, paddingRight: 5, width: 0, height: view.frame.size.height / 7)
I'll assign value into UILabel according to clicking button so i want to set chracter limit for the UILabel. According to code that i written it cropped data as i wanted but it didn't do that same action in the value. How can achieve of that could you help me out? Thanks.

What you want is not possible out of the box, and would be really strange in my opinion. Now the data and its presentation are separated, as they should be. Imagine, if your view controller size changed (the device got rotated, or your app is run on iPad in split mode, and the user resized the apps) - then you would be able to see more of your data. Because the label keeps the original data, everything would happen automatically. If it cropped the data to match its rendered representation, you would need to react to all such changes and reassign the original data to the label.
To provide a real world example - imagine you are looking at a huge (building sized) billboard, through a small (lets size, letter paper sized) window. You can see only a portion of the billboard, but everything is still there. In the same way, a UILabel is just a "window" onto the data provided.
Now, implementing this won't be trivial. Such a UIQuantumLabel* would need to take into consideration the width of each letter/digit/emoji/character in the data for current font at its current size (unless you were to limit it to monospaced fonts), take into consideration whether the automatic font scaling has happened and at what magnitude, whether the user has decreased/increased the font size in devices accessibility settings, and probably some more factors.
What is your use case for this? Why would you want to crop your data, and not keep the original?
*UIQuantumLabel because it reminded me of quantum mechanics (at least what I remember of it), where observing an object changes it state :)

Related

How to wrap text in Swift 5

Brand new to Xcode and Swift, but I'm trying to have a headline and subheading for a news type app I'm prototyping out. I can't get the text to wrap, leaving out most of the headline and brief summary. I need it to say
Unity with purpose. Amanda Gorman and Michelle Obama Discuss Art Identity and Optimism
Amanda Gorman captivated the world when she read her poem “The Hill We
Climb” at President Joe Biden and Vice President Kamala Harris’ Jan.
20 Inauguration ceremony.
All I get is:
Here is the code generating the labels, this is the headline but they are basically the same:
private let headlineLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
label.textColor = .white
label.font = UIFont.preferredFont(forTextStyle: .largeTitle) //Scales font automatically
label.adjustsFontForContentSizeCategory = true //Adjusts font sized based on settings user has
label.lineBreakMode = .byWordWrapping
label.numberOfLines = 0
return label
}()
I've tried setting numberOfLines = 0 as well as label.lineBreakMode = .byWordWrapping and neither seem to work.
How can I get the entire string for both the headline and subheading to be displayed?
Thanks!
The properties that you applied to your UILabel to allow wrapping looks correct, but
without seeing more of the code it's hard to tell. So I'm betting it's your horizontal layout constraints (if any). If you did not set an explicit frame to your view. Then ensure you're setting+activating vertical and horizontal constraints for your view.
// Important step to enable contraints
label.translatesAutoresizingMaskIntoConstraints = false
....
// Important that you add your view to the view hierarchy first before activating
// constraints. If not this will produce a runtime error.
self.view.addSubview(label)
....
NSLayoutConstraint.activate([
label.leadingAnchor.contraint(equalTo: self.view.leadingAnchor)
label.trailingAnchor.contraint(equalTo: self.view.trailingAnchor)
label.topAnchor.contraint(equalTo: self.view.topAnchor)
])
The .activate(:) is another important step and common pitfall. Ensure you're activating your contraints!
leadingAnchor is attaching the left side of your label's frame to the left side of your parent view.
trailingAnchor is doing the same thing, but for the right side.
Then finally we pin the label to the top of the parent view. You may want use .contraint(equalTo: .., constant: ..) to push your label down a little bit as well.
So, in the next update loop and when your parent view lays out the subviews. Your label's frame will be calculated based on your activated constraints. So in this case your label's frame will look something like this (x:0, y:0, width: 300, height: *height is intrinsic here) This is assuming a parent frame of (x: 0, y: 0, width: 300, height: 600)
You mentioned you're new to iOS, so I hope this info helps you in some way.

Moving Labels within External Screen

I'm wanting to move my labels wherever I want on my external display. (eg. displayRunsLabel at the top and centered or perhaps 40px from right and top)
I have my labels showing on the screen. I'm currently using text alignment.
What's the best way to do this?
Here's my external display:
ExternalDisplayScreenshot
Here's the code:
// CUSTOMISE VIEW
secondScreenView.backgroundColor = UIColor.black
displayWicketLabel.textAlignment = NSTextAlignment.left
displayWicketLabel.font = UIFont(name: "Arial-BoldMT", size: 200.0)
displayWicketLabel.textColor = UIColor.white
displayWicketLabel.frame = secondScreenView.bounds
secondScreenView.addSubview(displayWicketLabel)
displayRunsLabel.textAlignment = NSTextAlignment.center
displayRunsLabel.font = UIFont(name: "Arial-BoldMT", size: 200.0)
displayRunsLabel.textColor = UIColor.white
displayRunsLabel.frame = secondScreenView.bounds
secondScreenView.addSubview(displayRunsLabel)
displayOversLabel.textAlignment = NSTextAlignment.right
displayOversLabel.font = UIFont(name: "Arial-BoldMT", size: 200.0)
displayOversLabel.textColor = UIColor.white
displayOversLabel.frame = secondScreenView.bounds
secondScreenView.addSubview(displayOversLabel)
}
The position of a label or any other UIView is the origin of its frame, which is a CGPoint representing its top left corner.
So, you are currently saying:
displayWicketLabel.frame = secondScreenView.bounds
displayRunsLabel.frame = secondScreenView.bounds
displayOversLabel.frame = secondScreenView.bounds
Well, that's pretty silly, because you have given all three labels the same frame! Thus they overlay one another, and (as you rightly say) you can only read them by giving them different text alignments.
Instead, give them each a different frame — one that puts the label where you want it. For example:
displayWicketLabel.sizeToFit()
displayWicketLabel.frame.origin = CGPoint(x:30, y:20) // or whatever
displayRunsLabel.sizeToFit()
displayRunsLabel.frame.origin = CGPoint(x:30, y:100) // or whatever
displayOversLabel.sizeToFit()
displayOversLabel.frame.origin = CGPoint(x:30, y:200) // or whatever
Now, having said all that, a clear flaw in the code I just showed you is that we have hard-coded the positions arbitrarily, without taking account of what the size of the window is (the external display). For that reason, a much better way would be for you to learn about auto layout, which will let you lay the three labels out in nice positions automatically based on the size of the window.

Add label to point

I have followed this tutorial: https://www.raywenderlich.com/410-core-graphics-tutorial-part-2-gradients-and-contexts
I am trying to add a label over each point with the Int for the point, but it wont show. Adding code in the //Draw the circles on top of graph stroke
//Draw the circles on top of graph stroke
for i in 0..<graphPoints.count {
var point = CGPoint(x:columnXPoint(i), y:columnYPoint(graphPoints[i]))
point.x -= Constants.circleDiameter / 2
point.y -= Constants.circleDiameter / 2
let circle = UIBezierPath(ovalIn: CGRect(origin: point, size: CGSize(width: Constants.circleDiameter, height: Constants.circleDiameter)))
circle.fill()
//let label = UILabel(frame: CGRect(origin: point, size: CGSize(width: Constants.circleDiameter, height: Constants.circleDiameter)))
let label = UILabel()
label.frame.origin = CGPoint(x:columnXPoint(i), y:columnYPoint(graphPoints[i]))
label.text = "TDDDDDDDE"
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.black
self.addSubview(label)
self.view.addSubview(label)
}
Can anyone help me with adding the label? The label wont show, and I want it over the points.
Best guests, given there's not the full context. I see 3 possible reasons
1: You should replace
self.addSubview(label)
self.view.addSubview(label)
By
self.addSubview(label)
The second line will remove the label from view to add it to self.view, which might be a problem if they are different.
2: With the following line, you're telling the label to use autolayout ...but do not provide any constraint, so they might be there but not where you expected them to be.
label.translatesAutoresizingMaskIntoConstraints = false
To add constraints: see official Apple Doc, example below:
label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: aConstant).isActive = true
But 3: your label is initialised without a frame/an empty frame. Call sizeToFit on your label after having set the content, fonts, etc if you don't use autolayout. If you stick to autolayout, this point is not applicable.
And another point indirectly linked to your question: If you manage to get your code to work, you'll probably have too much labels: your code seems to be in the draw method, which means that you'll be instantiating new labels each time the view is rendered ...and the previous ones won't be removed. A better practice would be instanciate the labels when you load the data (if they depend on the data points), to position them in layoutSubviews or to skip the labels entirely and use NSAttributedString draw functions to render the text directly in the view.

Dynamically center 2 lines of text programmatically with roughly equal length per row

My situation is that I have a line of text that can vary in length due to localization. This will need to be displayed on the screen such that each line is roughly of equal length, and is centered.
This is my very long line.
Should look like this
This is my
very long line.
So I took a crack at this and got something that works the way I want it now.
I take a localized string, set it to an empty label, and find out what it's size is. (The orange is just for illustrative purposes)
With the size of the label, I then divide it by 1.8 which gives me some buffer room to account for inconsistent word sizes (again, I don't know what will be here in advance). Finally, I multiply the height by 2.0, and set that as my new frame. Finally, I add it to the view.
This has held up with a few sample strings, though it would need to be revised to handle more than 2 lines (currently, not an issue).
let text = NSLocalizedString("This is my very long line of text.", comment: "")
let instructionLabel = UILabel()
instructionLabel.text = text
instructionLabel.textAlignment = .center
instructionLabel.backgroundColor = .orange
instructionLabel.numberOfLines = 0
let size = instructionLabel.intrinsicContentSize
let newSize = CGSize(width: size.width / 1.8, height: size.height * 2.0)
let rect = CGRect(x: 20, y: 100, width: newSize.width, height: newSize.height)
instructionLabel.frame = rect
view.addSubview(instructionLabel)
Which produces the following output:
And an even longer one:
Just for some variety, this is the second string above, but in Arabic:
You could do this to set alignment.
myLabel.textAlignment = .center
Also set the number of lines to 0. And if you want a specific width, set the preferredMaxLayoutWidth property like so:
myLabel.preferredMaxLayoutWidth = 80
myLabel.numberOfLines = 0
If you want it to work for arbitrary localizations (assuming languages that use spaces), you would need an algorithm that split the text on spaces and then loop through each combination of top and bottom text, measuring its width, to see what gave the most evenly distributed sizing. This feels like overkill.
Having done a fair amount of localization, the better bet is to manually insert \n characters in the .strings file to adjust breaks that aren't visually pleasing. Relying on a fixed width will work for many languages, but won't give you the flexibility you're looking for.

How do you add an outline to text programmatically?

I have two apps, one with a UILabel and one using SpriteKit with a SKLabelNode. I'd like to add a black outline around the white text.
I can't find any outline or border properties or anything like that within swift. I've tried just creating new labels with slightly bigger, black font behind them but that didn't look right at all.
Here is my game with SpriteKit
title.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
title.text = "Tap to start!"
title.fontName = "Arial"
title.zPosition = 10
title.fontSize = 50
self.addChild(title)
And here is my other one that uses UILables (It's within a red rectangle so it's easier to see)
let rectangle = UIView(frame:CGRect(x: self.view.frame.size.width / 2 - 150, y: self.view.frame.size.height / 2 - 75, width: 300, height: 150))
rectangle.backgroundColor = UIColor.red
self.view.addSubview(rectangle)
let label = UILabel(frame: CGRect(x: rectangle.frame.size.width / 2 - 75, y: rectangle.frame.size.height / 2 - 10, width: 150, height: 20))
label.textAlignment = .center
label.text = "Tap to Start!"
label.textColor = UIColor.white
label.font = UIFont(name: "Arial", size: 20)
rectangle.addSubview(label)
How do I outline these labels?
For SKLabelNode's, there is no 'easy' way of outlining text. There is however a 'hack'. It involves adding 8 additional duplicate nodes around the label that are coloured black (or whatever colour you want the outline to be), have a zPosition less than the original and otherwise, be identical.
It's important that each label has the same font size as the original.
Your attempt at doing this involved increasing the font size of your one outline copy label. As you said in the question, it doesn't work. You have to use multiple labels so the effect appears seamlessly.
You would place one copy directly above the original, one directly below, one to the left, one to the right, then one on each corner (top right, top left, bottom right, and bottom left). This gives the effect of an outline, however is not efficient and should be taken with a grain of salt.
Note: Keep the amount shifted from the original consistent for all the copies, so the 'outline' is evenly sized.
This should work for UILabels too, however I think there might be an easier way to do this with UILabels.

Resources