I am using autolayout and text resizing with UILables and it works fine with width but not so much in the height.
If there is space for the text in the width but not in the height the text does not compress
Any Ideas how can I create constraints to do that? Or it that is not possible how can I find the minimum size of text that fits an rectangle?
You can set the font to automatically fill the size of a label, and optionally not go below a minimum font size. Just set adjustsFontSizeToFitWidth to YES. Check out the UILabel Class Reference if you need more information.
Although the boolean is called "adjustsFontSizeToWidth," it really means the largest size for the height of the label, that will stay on one line of the label (or however many lines you specify).
Subclassed UILabel and overrode layoutSubviews. Then each time the UILabel gets its size changed, the font size is recalculated:
import Foundation
import UIKit
class LabelWithAdaptiveTextHeight: UILabel {
override func layoutSubviews() {
super.layoutSubviews()
font = fontToFitHeight()
}
// Returns an UIFont that fits the new label's height.
private func fontToFitHeight() -> UIFont {
var minFontSize: CGFloat = DISPLAY_FONT_MINIMUM // CGFloat 18
var maxFontSize: CGFloat = DISPLAY_FONT_BIG // CGFloat 67
var fontSizeAverage: CGFloat = 0
var textAndLabelHeightDiff: CGFloat = 0
while (minFontSize <= maxFontSize) {
fontSizeAverage = minFontSize + (maxFontSize - minFontSize) / 2
if let labelText: NSString = text {
let labelHeight = frame.size.height
let testStringHeight = labelText.sizeWithAttributes(
[NSFontAttributeName: font.fontWithSize(fontSizeAverage)]
).height
textAndLabelHeightDiff = labelHeight - testStringHeight
if (fontSizeAverage == minFontSize || fontSizeAverage == maxFontSize) {
if (textAndLabelHeightDiff < 0) {
return font.fontWithSize(fontSizeAverage - 1)
}
return font.fontWithSize(fontSizeAverage)
}
if (textAndLabelHeightDiff < 0) {
maxFontSize = fontSizeAverage - 1
} else if (textAndLabelHeightDiff > 0) {
minFontSize = fontSizeAverage + 1
} else {
return font.fontWithSize(fontSizeAverage)
}
}
}
return font.fontWithSize(fontSizeAverage)
}
}
Swift 3.0 for anyone new coming here.
Thanks a lot @tymac for the original answer.
Really helped me not to worry about text sizes when making my apps universal.
import UIKit
import Foundation
class LabelWithAdaptiveTextHeight: UILabel {
override func layoutSubviews() {
super.layoutSubviews()
font = fontToFitHeight()
}
// Returns an UIFont that fits the new label's height.
private func fontToFitHeight() -> UIFont {
var minFontSize: CGFloat = 20
var maxFontSize: CGFloat = 250
var fontSizeAverage: CGFloat = 0
var textAndLabelHeightDiff: CGFloat = 0
while (minFontSize <= maxFontSize) {
fontSizeAverage = minFontSize + (maxFontSize - minFontSize) / 2
if let labelText: String = text {
let labelHeight = frame.size.height
let testStringHeight = labelText.size(attributes: [NSFontAttributeName: font.withSize(fontSizeAverage)]).height
textAndLabelHeightDiff = labelHeight - testStringHeight
if (fontSizeAverage == minFontSize || fontSizeAverage == maxFontSize) {
if (textAndLabelHeightDiff < 0) {
return font.withSize(fontSizeAverage - 1)
}
return font.withSize(fontSizeAverage)
}
if (textAndLabelHeightDiff < 0) {
maxFontSize = fontSizeAverage - 1
} else if (textAndLabelHeightDiff > 0) {
minFontSize = fontSizeAverage + 1
} else {
return font.withSize(fontSizeAverage)
}
}
}
return font.withSize(fontSizeAverage)
}
}
Here is an Objective-C implementation of tymac's answer for anyone interested.
(I was in a bit of a rush and didn't have time to fix the lower case Gs, Js, Ps and Qs being cut so I quickly subtracted two from the average text size in the calculation function, sorry.)
#import "LabelWithAdaptiveTextHeight.h"
#define DISPLAY_FONT_MINIMUM 6
#define DISPLAY_FONT_MAXIMUM 50
@interface LabelWithAdaptiveTextHeight ()
@end
@implementation LabelWithAdaptiveTextHeight
- (UIFont*)fontToFitHeight {
float minimumFontSize = DISPLAY_FONT_MINIMUM;
float maximumFontSize = DISPLAY_FONT_MAXIMUM;
float fontSizeAverage = 0;
float textAndLabelHeightDifference = 0;
while(minimumFontSize <= maximumFontSize){
fontSizeAverage = minimumFontSize + (maximumFontSize - minimumFontSize) / 2;
if(self.text){
float labelHeight = self.frame.size.height;
float testStringHeight = [self.text sizeWithAttributes:@{
NSFontAttributeName: [self.font fontWithSize:fontSizeAverage]
}].height;
textAndLabelHeightDifference = labelHeight - testStringHeight;
if(fontSizeAverage == minimumFontSize || fontSizeAverage == maximumFontSize){
return [self.font fontWithSize:fontSizeAverage- (textAndLabelHeightDifference < 0)];
}
if(textAndLabelHeightDifference < 0){
maximumFontSize = fontSizeAverage - 1;
}
else if(textAndLabelHeightDifference > 0){
minimumFontSize = fontSizeAverage + 1;
}
else{
return [self.font fontWithSize:fontSizeAverage];
}
}
else {
break; //Prevent infinite loop
}
}
return [self.font fontWithSize:fontSizeAverage-2];
}
- (void)layoutSubviews {
[super layoutSubviews];
self.font = [self fontToFitHeight];
}
@end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With