I have a multiple UILabels. When the device size changes the width and height of the labels changes, so I scale down the text so it fits inside the label. What I want to do is get the font size from one label, and set it as the font size of the other labels, so that everything fits inside the labels, but are also the same size.
This is my code for rescaling the text:
Name_Label.numberOfLines = 1
Name_Label.adjustsFontSizeToFitWidth = true
Name_Label.lineBreakMode = NSLineBreakMode.ByClipping
I can get the point size as other answers have suggested:
Year_Label.font.fontWithSize(Name_Label.font.pointSize)
But this does not work, and comments suggest that this does not return the font size. So how do I get the font size of a label that has just been scaled down, and use that to set the other labels font size?
UPDATE: When I print the following code the output is the same, however in the simulator the size is different.
Year_Label.font = Name_Label.font
You can use this class to fit labels size. Just tell that your label is this class. There you as well can set if it should check horizontally or vertically.
https://github.com/VolodymyrKhmil/BBBLibs/tree/master/BBBAutoresizedFontLabel
Hope it helps.
After doing more research, I found a function in Swift 3 that worked. However, to change the size you have to set the Minimum Font Scale (Which I forgot to do). Below is the code in Swift 2 that works:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
Name_Label.numberOfLines = 1
Name_Label.adjustsFontSizeToFitWidth = true
Name_Label.lineBreakMode = NSLineBreakMode.ByClipping
func adjustedFontSizeForLabel(label: UILabel) -> CGFloat {
let text: NSMutableAttributedString = NSMutableAttributedString(attributedString: label.attributedText!)
text.setAttributes([NSFontAttributeName: label.font], range: NSMakeRange(0, text.length))
let context: NSStringDrawingContext = NSStringDrawingContext()
context.minimumScaleFactor = label.minimumScaleFactor
text.boundingRectWithSize(label.frame.size, options: NSStringDrawingOptions.UsesLineFragmentOrigin, context: context)
let adjustedFontSize: CGFloat = label.font.pointSize * context.actualScaleFactor
return adjustedFontSize
}
Name_Label.font = Name_Label.font.fontWithSize(adjustedFontSizeForLabel(Name_Label))
Year_Label.font = Year_Label.font.fontWithSize(adjustedFontSizeForLabel(Name_Label))
House_Label.font = House_Label.font.fontWithSize(adjustedFontSizeForLabel(Name_Label))
}
Link to source: https://stackoverflow.com/a/38568278/6421669
Related
In my project, i am using scaling for UI components. I am able to scale the text for UIlabel like below and it's working in all device:
1. Autoshrinks - minimum font scale set it to 0.5
2. No of lines - 0
3. Enable dynamic type in attribute inspector
4. adjustFontSizeToWidth to true
But when i am trying to adjust font for UI Button using beolow steps and i am not able to scale the text for UI button.
button.titleLabel?.numberOfLines = 1 // Tried with 0 also
button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.lineBreakMode = // tried differenet linebreakmode
Could anyone have an idea of scaling UI Button text?
Are you sure it's not working?
Edit - After comments...
UIKit elements such as UILabel / UIButton / etc to not have a built-in "auto-adjust font height" property.
I don't work for Apple, so just guessing that is (at least in part) due to the fact that, in general...
Based on screen height, the UI is designed to either:
provide more or less information, e.g. more rows in a table, or
adjust vertical spacing between elements
That doesn't mean you can't or shouldn't adjust your font sizes... it just means you have to do it manually.
Couple options:
set the font-size at run-time, as suggested by Duncan
use a UIAppearance proxy to set the font-size, again at run-time
in either case, you could use a height-to-fontSize table or a "percentage" calculation.
Another option would be a custom class that sets the font-size based on the constrained button height.
Here's a simple example (note: for demonstration purposes only):
class AutoFontSizeButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
guard let fnt = titleLabel?.font else { return }
// default system type button has 6-pts top / bottom inset
// and font size is 15/18ths of that height
let h = ((bounds.height - 12.0) * (15.0 / 18.0)).rounded()
let fs = fnt.pointSize
if h != fs {
titleLabel?.font = UIFont(descriptor: fnt.fontDescriptor, size: h)
}
}
}
Result - the top three (yellow) buttons are 30, 40 and 50-points in height, with the default font-size of 15. The bottom three (green) buttons are again 30, 40 and 50-points in height, but the font-size is automatically set at run-time:
I don't think there is a way to get the font to auto-size. However, if you set the button's titleLabel.font to a specific font size the button will update to use the new font size, including resizing the button.
Use code like this:
let size: CGFloat = useLargeFont ? 50.0 : 17.0 //Change as needed
if let buttonFont = button.titleLabel?.font {
button.titleLabel?.font = buttonFont.withSize(size)
}
I have a UILabel that is designed to expand in height when the width of the text's CGSize is greater than the width of the label. I accomplish that with this code:
func viewHeight(_ locationName: String) -> CGFloat {
let locationName = tappedLocation[0].name
var size = CGSize()
if let font = UIFont(name: ".SFUIText", size: 17.0) {
let fontAttributes = [NSAttributedStringKey.font: font]
size = (locationName as NSString).size(withAttributes: fontAttributes)
}
let normalCellHeight = horizontalStackViewHeightConstraint.constant
let extraLargeCellHeight = horizontalStackViewHeightConstraint.constant + 20.33
let textWidth = ceil(size.width)
let cellWidth = ceil(nameLabel.frame.width)
if textWidth > cellWidth {
return extraLargeCellHeight
} else {
return normalCellHeight
}
}
Lines = 0 and line break style = Word Wrap:
The label lives inside a vertical stackView, and is constrained to its top, leading and trailing edges and a stackView beneath it. The height of the label and the UIView properly expand in height when the CGSize width of the text is longer than the width of the label. All well and good.
However, the words do not wrap consistently. This behavior is intentional:
Bobby Mao's Chinese Kitchen & Bar:
XL cell. Width: 184.0,
Text width: 287.0
This behavior is not (why isn't "steak" on the prior line?):
Ruth's Chris Steak House:
XL cell. Width: 184.0,
Text width: 204.0
And neither is this (why didn't Gina wrap if it's over the label width parameter?):
Ristorante Mamma Gina:
XL cell. Width: 184.0,
Text width: 191.0
I have also set a temporary background color on my label to ensure that it does, in fact correspond to the intended width. The label in this example creates another line when the label's width is exceeded, but the text does not wrap:
I have read the other entries on Stack Overflow about word wrapping. I don't believe this is a duplicate. I do not have trouble creating two lines for my text. I don't have trouble with word wrapping occurring. I have trouble with how and when it is occurring.
I think the intent is clear... what am I missing?
When I draw an attributed string with a fixed line height with Text Kit, the characters always get aligned to the bottom of the line fragment. While this would make sense on one line with characters varying in size, this breaks the flow of the text with multiple lines. The baselines appear decided by the largest descender for each line.
I've found an article from the people behind Sketch explaining this exact problem in a bit more detail and showing what their solution does, but obviously not explaining how they achieved this.
This is what I want basically:
When showing two lines with a large line height, this result is far from ideal:
The code I'm using:
let smallFont = UIFont.systemFont(ofSize: 15)
let bigFont = UIFont.systemFont(ofSize: 25)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.minimumLineHeight = 22
paragraphStyle.maximumLineHeight = 22
var attributes = [
NSFontAttributeName: smallFont,
NSParagraphStyleAttributeName: paragraphStyle
]
let textStorage = NSTextStorage()
let textContainer = NSTextContainer(size: CGSize(width: 250, height: 500))
let layoutManager = NSLayoutManager()
textStorage.append(NSAttributedString(string: "It is a long established fact that a reader will be ", attributes:attributes))
attributes[NSFontAttributeName] = bigFont
textStorage.append(NSAttributedString(string: "distracted", attributes:attributes))
attributes[NSFontAttributeName] = smallFont
textStorage.append(NSAttributedString(string: " by the readable content of a page when looking at its layout.", attributes:attributes))
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
let textView = UITextView(frame: self.view.bounds, textContainer:textContainer)
view.addSubview(textView)
I managed to get this working, but had to drop support for iOS 8 and macOS 10.10 unfortunately.
If you implement the following delegate call of the NSLayoutManager, you get to decide what to do with the baselineOffset for each line fragment:
optional func layoutManager(_ layoutManager: NSLayoutManager,
shouldSetLineFragmentRect lineFragmentRect: UnsafeMutablePointer<CGRect>,
lineFragmentUsedRect: UnsafeMutablePointer<CGRect>,
baselineOffset: UnsafeMutablePointer<CGFloat>,
in textContainer: NSTextContainer,
forGlyphRange glyphRange: NSRange) -> Bool
When the NSTextStorage is created and for each subsequent change, I enumerate all used font, calculate it's default line height (NSLayoutManager.defaultLineHeightForFont()) and store the biggest line height. In the implementation of the above mentioned delegate method I check the current line height of the NSParagraphStyle for the provided line fragment and align the font's line height within that value. From there the baseline offset can be calculated with the knowledge that the baseline sits between the font's ascender and descender. Update the baselineOffset value with baselineOffset.memory(newOffset) and everything should be aligned as you'd like.
Note: I'm not going in too much detail about the actual code used to implement this because I'm not sure I'm using the right values throughout these calculations. I might update this in the near future when the whole approach is tried and proven.
Update: Implementation of adjusting baseline. Every time the textContainer changes I recalculate the biggest line height and biggest descender. Then I basically do this in the layout manager's delegate function:
var baseline: CGFloat = (lineFragmentRect.pointee.height - biggestLineHeight) / 2
baseline += biggestLineHeight
baseline -= biggestDescender
baseline = min(max(baseline, 0), lineFragmentRect.pointee.height)
baselineOffset.pointee = floor(baseline)
I have a multi line label with a set size (300 x 300).
I want to adjust the label's font size programmatically according to how long the label's text is and how big the label is.
Here are 2 examples of the same sized labels with different length text strings
Refer to NSString.boundingRectWithSize:options:attributes:. Here you need to decrement the font size stepwise until the string will fit into the original frame.
var paragraph = NSMutableParagraphStyle()
let layout = [NSFontAttributeName:NSFont.systemFontOfSize(0), NSParagraphStyleAttributeName: paragraph, ]
paragraph.lineBreakMode = NSLineBreakMode.ByWordWrapping
var a = NSString(string: "My long text")
let rect = NSMakeSize(30, 20)
let bb = a.boundingRectWithSize(rect, options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: layout)
Place the above in Playground and modify rect and the font to see what happens.
I know the Question is old but....
Just use the option AutoShrink and set the minimum font size/scale in the Attributes Inspector, it will scale the font as large as possible to fit the size of the label.
I've created a Label with the following code :
func setupValueLabel() {
valueLabel.numberOfLines = 1
valueLabel.font = UIFont(name: "Avenir-Black", size: 50)
valueLabel.adjustsFontSizeToFitWidth = true
valueLabel.clipsToBounds = true
valueLabel.backgroundColor = UIColor.greenColor()
valueLabel.textColor = valuesColor
valueLabel.textAlignment = NSTextAlignment.Center
}
I don't really understand why but the label is not vertically centered :
Do I have to do anything specific so it can be centered ?
The problem is that font size is shrunk by adjustsFontSizeToFitWidth = true, but it does not adjust the lineHeight automatically. It remains to be for original font size that is 50.
By default, the text is aligned to its baseline. you can adjust it with baselineAdjustment property.
In your case, you should set it to UIBaselineAdjustment.alignCenters.
valueLabel.baselineAdjustment = .alignCenters
Thanks to #rintaro, it works finally.
One more thing for my case, it didn't work because I was setting ByWordWrapping. I had to set lineBreakMode as ByClipping.