UILabel centered text CGRect? - swift

I have a UILabel with centered text. Single line, not attributed. I want to get the CGRect for the actual text in the label (it spans the whole width of a iOS device). Some of the text is dynamic in the text, so the width of the text in the label can change from time to time. So I want to get the position of the text so that I can place an image next to the first character.
I did this in the past but can't remember what I did to do it.

I ended up using UILabel.textRect. It works perfectly for me.
let myRect:CGRect = _topBanner.textRect(forBounds: _topBanner.bounds, limitedToNumberOfLines: 1)
_icon.frame.origin.x = myRect.origin.x - _icon.frame.size.width + 50
_icon.frame.origin.y = _topBanner.frame.origin.y + 8

Try creating a temporary UILabel just to get the text width:
let fullWidthLabel: UILabel = ...
let tmpLabel = UILabel()
tmpLabel.text = fullWidthLabel.text
tmpLabel.font = fullWidthLabel.font
tmpLabel.sizeToFit()
let textWidth = tmpLabel.frame.width
Use textWidth to solve your original problem.

Related

How to smoothly change text in UILabel

I have some UILabels next to each other containing text.
It could be:
2x-5 = 3
I can drag "-5" to the other side.
2x = 3-5
It then moves in to place next to "3" and then I change the sign from - to +, within a UIView.animate() function.
Because "+" is a little bit wider than "-" the text goes past the limits of the UILabel, which cuts of the text in one side.
So I resize the labels width, but it is visible when using the application.
I have tried changing the size of the label before I change the sign, just adding a lot of space.
But it still cuts some of the text off.
Here is the code:
let v = UILabel()
v.text = "+5"
UIView.animate(withDuration: 0.5, animations: {
v.frame.size.width = v.intrinsicContentSize.width + 20
v.text = "-5"
v.font = UIFont.boldSystemFont(ofSize: 45)
v.textAlignment = .center
v.frame.size.width = v.intrinsicContentSize.width
v.frame.size.height = v.intrinsicContentSize.height
}
Optimally I want the label not to cut off any of the text when the width of the text is changed.
(Edit: removed other question unrelated to main question)
Thank you in advance for any help!

UILabel lineBreakMode set to .ByTruncatingTail causes RTL arabic text to appear incorrectly

I have a UILabel with RTL Arabic text inside. Everything appears correctly as long as I don't change the lineBreakMode. I need it to clip instead of byTruncatingTail in order to avoid showing the ellipses character as I'm trying to show a gradient mask on the left edge.
I've tried changing contentMode, alignment etc but nothing helps. The right side of the label seems to start text from the middle somewhere, instead of from the start (i.e. the right most character in the text).
This is what I see with lineBreakMode = .byClipping
And this is what I see when I remove the lineBreakMode
Here's the code
let arabicLabel = UILabel(frame: .zero)
arabicLabel.semanticContentAttribute = .forceRightToLeft
arabicLabel.numberOfLines = 1
//arabicLabel.lineBreakMode = .byClipping
arabicLabel.text = "عِنْدَمَا1 عِنْدَمَا2 عِنْدَمَا3 عِنْدَمَا4 عِنْدَمَا5 عِنْدَمَا6 قَدِمْتُ عَلَى (صَاحِبِي) عِنْدَمَا7 عِنْدَمَا8 عِنْدَمَا9 عِنْدَمَا10 عِنْدَمَا عِنْدَمَا قَدِمْتُ عَلَى (صَاحِبِي)"
arabicLabel.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(arabicLabel)
After wasting enough time on this, I ended doing it the ugly way, by overriding drawText and adding an arbitrary amount of padding to effectively hide the ellipses character and handling it for both RTL and LTR. I could have based this on the semanticContentAttributes instead, but in my case the label will only ever be right aligned when it contains RTL text. I've also had to override text to automatically detect the dominant language set, and alter the alignment automatically.
public override func drawText(in rect: CGRect) {
let extraWidth = fadeLength + 50
var newRect = rect
if self.textAlignment == .right {
newRect.origin.x -= extraWidth
newRect.size.width += extraWidth
} else {
newRect.size.width += extraWidth
}
super.drawText(in: newRect)
}

shadow not behind text

I've made a function that is called in drawRect() inside a seperate made class for a Label. However, this draws only behind the text, and not behind the background of the label. I want to have a shadow behind the background of the label, not the text. How can I fix this? The same happens in a seperate made class for a View.
let COLOR_SHADOW_COLOR: CGColor = UIColor.gray.cgColor
let COLOR_SHADOW_OFFSET = CGSize(width: 2, height: -2)
let COLOR_SHADOW_RADIUS: CGFloat = 5
let COLOR_SHADOW_OPACITY: Float = 1.0
func setShadow(on object: UIView) {
object.layer.shadowColor = COLOR_SHADOW_COLOR
object.layer.shadowOpacity = COLOR_SHADOW_OPACITY
object.layer.shadowOffset = COLOR_SHADOW_OFFSET
object.layer.shadowRadius = COLOR_SHADOW_RADIUS
}
Fixed my own problem. The background was the same color as the text, but with a lower opacity. Xcode thinks that it should draw around text then. After using the pipet on the same color, it did made the shadow behind the label and view

How to insert Custom Label with text into View with Animation?

Im making a small boxing app. This whole week iv been trying to work out how to solve my current problem above, with no luck :(.
Summary
Basically Its a 1 v 1 game. Each time the user hits the other player I want to make a tiny number pop up near him and float up and dissappear.
Its sort of like when you play MMORPG's and when you do dmg you see how much you did. See image below for an example!
So basically everytime the user hits the other player I want a little number to pop up on the screen to show the dmg and then float up and disappear.
Details
I am building the game on a simple UIView
The label is a UILabel
Anywhere How I can achieve this?
Thank you!
Create a label, then use UIView.animateWithDuration to animate it.
let label = UILabel(frame: CGRect(origin: point/*The point where you want to add your label*/, size: CGSize(width: 50, height: 50)))
label.text = "+1"
label.font = UIFont()//Your font
label.textColor = UIColor.blueColor()
label.alpha = 1
self.view.addSubview(label)
UIView.animateWithDuration(1) {
label.center = CGPoint()//The point where you want your label to end up
label.alpha = 0
}
Edit: As mentioned in the comments, you asked for how to create the label at a random point. Try this:
let screenWidth = self.view.frame.width
let screenHeight = self.view.frame.height
let randomX = CGFloat(arc4random_uniform(UInt32(screenWidth)))
let randomY = CGFloat(arc4random_uniform(UInt32(screenHeight)))
let point = CGPoint(x: randomX, y: randomY)
As you will notice, for the width and height, I use the view's frame's width and height. You may want to use the view's bounds. For more on this, check out this SO Post.

UIButton label text is being clipped

I have a UIButton built in Interface Builder that has a default label. In Xcode, I'm changing the label text dynamically like so:
myButton.titleLabel.text = #"this is the new label";
However, when the text updates, the new string is being clipped down to the same size as the original string and ends up looking like:
this...label
Anyone know why this is happening?
You should use setTitle:forState: to change the title of a UIButton. If you change the title yourself, the button has no indication that it needs to resize the label – you'd end up having to do something like this:
myButton.titleLabel.text = #"this is the new label";
[myButton setNeedsLayout];
but I'm not even sure that would work in all cases. Methods like setTitle:forState: are provided so that you can provide titles for multiple states without having to update the button manually, and so that the button knows that it needs to be laid out with a new title.
Try using the button's setTitle method (rather than setting the title directly on the label). It should force the title label to be resized.
Objective C:
[myButton setTitle:#"This is the text" forState:UIControlStateNormal];
Or in Swift:
myButton.setTitle("This is the text", for: .normal)
An alternative solution is to let the UIButton's inner UILabel to shrink the font size, as UILabels can do :
button.titlelabel.minimumFontSize = 8.0; // or some more adequate size
self.buttonWithLongTitle.titleLabel.adjustsFontSizeToFitWidth = YES;
Call sizeToFit on your button. This will resize the button to fit the text.
If that didn't work you can always determine the string size and adjust the button frame width. In that case you are sure it will fit.
// Calculate the size
CGSize buttonSize = [#"My text.." sizeWithFont:[UIFont systemFontOfSize:15.0]
constrainedToSize:someSize lineBreakMode:UILineBreakModeWordWrap];
// Do whatever you want with the "buttonSize", you can for example adjust your button's frame width
Solution in Swift 4.2
yourButton.titleLabel?.minimumScaleFactor = 0.5 //set whatever you want here to scale
yourButton.titleLabel?.adjustsFontSizeToFitWidth = true
Solution for Objective C
[yourButton.titleLabel setMinimumScaleFactor:0.5];
[yourButton.titleLabel setAdjustsFontSizeToFitWidth:YES];
This worked for me while setting my button up programmatically with all other constraints.
yourButton.widthAnchor.constraint(equalToConstant: yourButton.intrinsicContentSize.width).isActive = true
You can also add "padding" like so
yourButton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)
Use Autoresizing mask if not selected.
None of the solutions above worked for me. What did was sublclassing UIButton and re-setting the frame of the titleLabel:
Context: My font was Josefin Sans and it does clipping the top part of "Ñ". I can imagine it could happen with any other text using a different font.
open class Button: UIButton {
open override func layoutSubviews() {
super.layoutSubviews()
guard let titleLabel = titleLabel else {
return
}
// Play around to get these numbers
let heightToAdd: CGFloat = 15
let widthToAdd: CGFloat = 100
titleLabel.frame = CGRect(
origin: CGPoint(
x: titleLabel.frame.origin.x - widthToAdd / 2,
y: titleLabel.frame.origin.y - heightToAdd / 2),
size: CGSize(
width: titleLabel.frame.width + widthToAdd,
height: titleLabel.frame.height + heightToAdd
)
);
}
}
Play around with the values widthToAdd and heightToAdd or make them injectable when setting a new title.
It is not a superb solution, but it was the only think that worked for me.