How to smoothly change text in UILabel - swift

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!

Related

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)
}

How to use frame to set x position in loop on Swift

I have an array that count is between 1 to 3 varies.
I want to frame them in the middle of the screen with a certain distance
Also the width of the labels is fixed
let array = ["some1", "some2", "some3"]
func setLabel(){
var i = -1
for text in array{
i += 1
let label = UILabel()
label.fram = CGRect(x: screenWidth/2 - (CGFloat(i)*50) + 25, y: 100, width: 50 , height: 20)
label.text = text
addSubview(label)
}
}
You can use stackview as suggested by #matt. If you don't want to use stackview you can calculate the x position by array count and label index like this
let array = ["some1", "some2", "some3"]
func setLabel(){
var i:CGFloat = 0
for text in array{
i += 1
let label = UILabel()
label.backgroundColor = .red
label.frame = CGRect(x: (view.bounds.width/CGFloat(array.count+1))*i-25, y: 100, width: 50 , height: 20)
label.text = text
view.addSubview(label)
}
}
The really easy way to do this is to put the labels (one, two, or three of them) into a centered UIStackView. You would still need to know the desired fixed width of a label and apply a width constraint to each label, but everything else will just take care of itself.
Just as a demonstration, I used an arbitrary width of 100, with the text for all three labels being just "UILabel" as in your image. Here's what it looks like with one label:
Here's what it looks like with three labels:
Here's the code I used (sv is the stack view, which has already been configured in the storyboard; n is how many labels we want):
for _ in 1...n {
let lab = UILabel()
lab.text = "UILabel"
lab.textAlignment = .center
lab.translatesAutoresizingMaskIntoConstraints = false
lab.widthAnchor.constraint(equalToConstant: 100).isActive = true
sv.addArrangedSubview(lab)
}
So all you really need to do is get the right text from your array as we loop, and you're done. The point is that you don't need to think about frames at all! The UIStackView imposes the correct the frames for us.
NOTE Although I said above that the stack view was "configured in the storyboard", that has nothing to do with the answer. You can create and configure the stack view in code just as well, without changing anything about my answer. The point is that the stack view already knows how to receive an arbitrary number of views, space them out evenly, and center them as a whole. That is what it is for. So if, as your question implies, you can't manage the arithmetic to assign the views a frame yourself, why not let the stack view do it for you?

UILabel centered text CGRect?

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.

UIScrollView wrong offset

I have several labels inside an scrollview. Each label is an "page". But the offset isn't right. And I can't find the problem.
My code:
let quantity = 10;
var width : CGFloat = 0.0;
override func viewDidLoad() {
super.viewDidLoad()
width = self.scrView.frame.width;
for i in 0..<quantity {
let xPos = width * CGFloat(i);
let label = UILabel(frame: CGRect(x: xPos, y: 0, width: width, height: self.scrView.frame.height))
label.textAlignment = .center
label.font = UIFont(name: "Helvetica Neue", size: 50)
label.textColor = UIColor.white
label.text = "\(i + 1)"
label.backgroundColor = .randomColor()
scrView.addSubview(label);
}
scrView.contentSize.width = width * CGFloat(quantity + 1);
}
The pink color on the left side is from the previous "page".
And again, note the purple color on the left side from the previous (number 8) page.
The first page seems fine. However the higher the number the greater the offset. I want to cover the whole visible area for 1 label.
Does somebody knows an solution?
Your code works fine for me.
Note that the size of the scrollView can change after viewDidLoad as a result of Auto Layout. A better place to set up your scrollView would be in an override of viewDidLayoutSubviews. The size of the scrollView will be established by then. But be careful, because viewDidLayoutSubviews will run multiple times, so make sure you only set up your scrollView once.
This is not your bug, but your contentSize.width is too big. You don't need to add 1 to quantity since there are exactly quantity labels in your scrollView content area.
scrView.contentSize.width = width * CGFloat(quantity)
Also, Swift doesn't need the ; at the ends of lines, so delete those.

How can I vertically align my status bar item text?

Left one on the picture is the date and time from Apple and one is my application one. As you may see the text of my application appears lower than the Apple one. Which doesn't look pretty. How can this be resolved?
self.statusBarItem.image = nil
self.statusBarItem.button?.imagePosition = .NoImage
self.statusBarItem.button?.title = "Sun 5 Jun 13:35"
You could do it in offset. You cannot do it like this since it needs to be done on the status item button bar.
button.frame = CGRectMake(0.0, 0.5, button.frame.width, button.frame.height)
I wanted to make a quick addition to this. While the accepted answer does indeed work based on the context of the question. It will not work if you have an image, as this moves everything (image and text). I have found for some reason, the image is vertically centered, but the text is not.
What you actually want to do in that case is set NSAttributedString.Key.baselineOffset, where you derive the baseline value as follows
var displayScale: CGFloat = 1.0
let pstyle = NSMutableParagraphStyle()
pstyle.alignment = .center;
let aStr = NSAttributedString(string: str, attributes: [NSAttributedString.Key.paragraphStyle: pstyle, NSAttributedString.Key.font: font])
if let main = NSScreen.main {
displayScale = main.backingScaleFactor
if displayScale <= 0.0 {
displayScale = 1.0
}
}
let textHeight = aStr.size().height
baselineOffset = ((font.ascender - font.descender) - textHeight) / displayScale
button.attributedTitle = = NSAttributedString(string: str, attributes: [NSAttributedString.Key.paragraphStyle: pstyle, NSAttributedString.Key.baselineOffset: baselineOffset, NSAttributedString.Key.font: font])
Disclaimers here are that I'm not completely sure about the displayScale part. I theorize this is needed to correct for pixels/points.
I've only added this answer because I was struggling to find a good answer for this. I got my inspiration to do this based on this: Center two fonts with different different sizes vertically in an NSAttributedString
If this technique is wrong, I'd be interested to know so I can fix it.