How to stop a time UILabel from resizing at every time increment?
Asked Answered
M

2

12

I have a stopwatch feature in my app that uses a centered attributed UILabel with a proportionally spaced font to render time. At every time increment the width of the label changes, creating a bouncing effect that looks especially bad at fast speeds. Here is an example.

How can I fix this?

iOS 9 UPDATE

It is now a one-liner:

UIFont.monospacedDigitSystemFontOfSize(17, weight: UIFontWeightRegular)

Also, last time I tried, the solution below did not work for iOS 9. Wasted quite a bit of time debugging before stumbling on this in the header.

SOLUTION

Turned out to be trivial with Text Kit in iOS 7.

Make sure Core Text is imported:

#import <CoreText/CoreText.h>

Create a setting that converts proportional numbers into monospaced:

NSArray *monospacedSetting = @[@{UIFontFeatureTypeIdentifierKey: @(kNumberSpacingType),
                                 UIFontFeatureSelectorIdentifierKey: @(kMonospacedNumbersSelector)}];

Create a new font descriptor by appending the current one used by UILabel:

UIFontDescriptor *newDescriptor = [[timeLabel.font fontDescriptor] fontDescriptorByAddingAttributes:@{UIFontDescriptorFeatureSettingsAttribute: monospacedSetting}];

Update label's font:

// Size 0 to use previously set font size
timeLabel.font = [UIFont fontWithDescriptor:newDescriptor size:0];
Mucker answered 5/2, 2014 at 6:10 Comment(2)
Post the relevant code for drawing the text.Ayo
This is a great solution for iOS 9 too where the standard system font has changed to San Fransisco and has proportional-width numbersKeri
B
2

Use a monospaced font or pass parameters when creating the font which force monospaced numbers:

//kNumberSpacingType 6
//kMonospacedNumbersSelector 0
NSArray *setting = @[
                         @{
                             UIFontFeatureTypeIdentifierKey: @(6),
                             UIFontFeatureSelectorIdentifierKey: @(0)
                             }
                         ];

return [UIFont fontWithDescriptor:[[self fontDescriptor] fontDescriptorByAddingAttributes:@{UIFontDescriptorFeatureSettingsAttribute : setting}] size:0.0];
Bortz answered 5/2, 2014 at 9:55 Comment(0)
F
3

For future readers:

This is how to enable monospaced numbers on iOS 9 with San Francisco:

let originalFont = UIFont.systemFontOfSize(17)
let originalFontDescriptor = originalFont.fontDescriptor()

let fontDescriptorFeatureSettings = [
    [
    UIFontFeatureTypeIdentifierKey: kNumberSpacingType,
    UIFontFeatureSelectorIdentifierKey: kMonospacedNumbersSelector
    ]
]

let fontDescriptorAttributes = [UIFontDescriptorFeatureSettingsAttribute: fontDescriptorFeatureSettings]    
let fontDescriptor = originalFontDescriptor.fontDescriptorByAddingAttributes(fontDescriptorAttributes)
let font = UIFont(descriptor: fontDescriptor, size: 0)

Edit:

Also available on GitHub: https://github.com/salutis/swift-font-monospaced-digits

Fourierism answered 22/6, 2015 at 13:49 Comment(2)
Works in Swift 2.0. Thanks! I extended UIFont with a function monospacedFont that contains your code, using self as the originalFont. That way I can easily get the monospaced version of any label in the app.Scratches
@Scratches Yep, same here.Albanian
B
2

Use a monospaced font or pass parameters when creating the font which force monospaced numbers:

//kNumberSpacingType 6
//kMonospacedNumbersSelector 0
NSArray *setting = @[
                         @{
                             UIFontFeatureTypeIdentifierKey: @(6),
                             UIFontFeatureSelectorIdentifierKey: @(0)
                             }
                         ];

return [UIFont fontWithDescriptor:[[self fontDescriptor] fontDescriptorByAddingAttributes:@{UIFontDescriptorFeatureSettingsAttribute : setting}] size:0.0];
Bortz answered 5/2, 2014 at 9:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.