I have a layout with a UILabel
placed above a fixed width view, shown below as a grey rectangle.
I am using next code to calculate kerning (by creating NSString extension).
This extension is using quicksort idea of pivot to quickly find kerning that makes the string fit into the needed width.
Please note that kerning less than -3.0 makes ugly characters overlap, so if string is not fitting with kerning = -3, the algorithm just returns -3. Of course you can set bigKern variable to smaller value.
I checked it against UITabBarItem's (Apple uses kerning on tab bar item labels), and my implementation is very similar.
Hope you like it.
@implementation NSString (Extension)
- (CGFloat)kernForFont:(UIFont *)font toFitWidth:(CGFloat)width
{
CGSize size = CGSizeMake(CGFLOAT_MAX, font.pointSize*2); // Size to fit.
const CGFloat threshold = 0.1;
CGFloat bigKern = -3.0, smallKern = 0.0, pivot = 0.0;
NSMutableDictionary *attrs = [NSMutableDictionary new];
attrs[NSFontAttributeName] = font;
while (true) {
attrs[NSKernAttributeName] = @(pivot);
CGRect frame = [self boundingRectWithSize:size
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attrs
context:nil];
CGFloat diff = width - frame.size.width;
if (diff > -0.5) {
// String is fitting.
if (pivot == 0.0) // Fits without kerning.
return pivot;
else if (smallKern - bigKern <= threshold)
return pivot; // Threshold is reached, return the fitting pivot.
else {
// Pivot is fitting, but threshold is not reached, set pivot as max.
bigKern = pivot;
}
}
else {
// String not fitting.
smallKern = pivot;
if (smallKern - bigKern <= threshold)
return bigKern;
}
pivot = (smallKern + bigKern) / 2.0;
}
return bigKern;
}
@end
Example usage, for custom UITabBarItems:
// I have a tabBarItem of type UITabBarItem. textColor is a UIColor.
NSString *title = tabBarItem.title;
CGFloat textLabelWidth = tabBar.frame.size.width / (CGFloat)(self.tabBar.items.count) - 6.0; // 6 is padding.
UIFont *font = [UIFont systemFontOfSize:10.0];
CGFloat kern = [title kernForFont:font toFitWidth:textLabelWidth];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.alignment = NSTextAlignmentCenter;
NSDictionary *attrs = @{
NSFontAttributeName: font,
NSKernAttributeName: @(kern),
NSForegroundColorAttributeName: textColor,
NSParagraphStyleAttributeName: paragraphStyle
};
textLabel.attributedText = [[NSAttributedString alloc] initWithString:title attributes:attrs];