Как получить одноразмерные числа в UILabel на iOS 9
На WWDC 2015 состоялся сеанс, посвященный новому системному шрифту "Сан-Франциско" в iOS 9. В нем по умолчанию используется пропорциональное отображение чисел вместо разнесенных чисел при связывании с iOS 9 SDK. На NSFont есть удобный инициализатор, который называется NSFont.monospacedDigitsSystemFontOfSize(mySize weight:)
это можно использовать для явного включения отображения моноширинного числа.
Однако я не мог найти UIKit
эквивалент для этого на UIFont
,
7 ответов
Удобный UIFont
расширение:
extension UIFont {
var monospacedDigitFont: UIFont {
let oldFontDescriptor = fontDescriptor()
let newFontDescriptor = oldFontDescriptor.monospacedDigitFontDescriptor
return UIFont(descriptor: newFontDescriptor, size: 0)
}
}
private extension UIFontDescriptor {
var monospacedDigitFontDescriptor: UIFontDescriptor {
let fontDescriptorFeatureSettings = [[UIFontFeatureTypeIdentifierKey: kNumberSpacingType, UIFontFeatureSelectorIdentifierKey: kMonospacedNumbersSelector]]
let fontDescriptorAttributes = [UIFontDescriptorFeatureSettingsAttribute: fontDescriptorFeatureSettings]
let fontDescriptor = self.fontDescriptorByAddingAttributes(fontDescriptorAttributes)
return fontDescriptor
}
}
Использование с @IBOutlet
свойства:
@IBOutlet private var timeLabel: UILabel? {
didSet {
timeLabel.font = timeLabel.font.monospacedDigitFont
}
}
Последняя версия на GitHub.
Теперь это доступно в UIFont
начиная с iOS 9:
+ (UIFont *)monospacedDigitSystemFontOfSize:(CGFloat)fontSize weight:(CGFloat)weight NS_AVAILABLE_IOS(9_0);
например:
[UIFont monospacedDigitSystemFontOfSize:42.0 weight:UIFontWeightMedium];
или в Swift:
UIFont.monospacedDigitSystemFont(ofSize: 42.0, weight: UIFontWeightMedium)
Принятое решение прекрасно работает, но было сбой при оптимизации компилятора, установленной на Быстрое (по умолчанию для сборок выпуска). Переписал код, подобный этому, и теперь его нет:
extension UIFont
{
var monospacedDigitFont: UIFont
{
return UIFont(descriptor: fontDescriptor().fontDescriptorByAddingAttributes([UIFontDescriptorFeatureSettingsAttribute: [[UIFontFeatureTypeIdentifierKey: kNumberSpacingType, UIFontFeatureSelectorIdentifierKey: kMonospacedNumbersSelector]]]), size: 0)
}
}
В Swift 4 было довольно много переименований, поэтому атрибуты теперь выглядят так:
let fontDescriptorAttributes = [
UIFontDescriptor.AttributeName.featureSettings: [
[
UIFontDescriptor.FeatureKey.featureIdentifier: kNumberSpacingType,
UIFontDescriptor.FeatureKey.typeIdentifier: kMonospacedNumbersSelector
]
]
]
Примечание: метод в текущем принятом ответе начал падать для меня в Xcode 7.3 (Swift 2.2), только в сборках выпуска. Устранение посредника monospacedDigitFontDescriptor
Переменная расширения устраняет проблему.
extension UIFont {
var monospacedDigitFont: UIFont {
let fontDescriptorFeatureSettings = [[UIFontFeatureTypeIdentifierKey: kNumberSpacingType, UIFontFeatureSelectorIdentifierKey: kMonospacedNumbersSelector]]
let fontDescriptorAttributes = [UIFontDescriptorFeatureSettingsAttribute: fontDescriptorFeatureSettings]
let oldFontDescriptor = fontDescriptor()
let newFontDescriptor = oldFontDescriptor.fontDescriptorByAddingAttributes(fontDescriptorAttributes)
return UIFont(descriptor: newFontDescriptor, size: 0)
}
}
Пример использования Swift 5.2 после принятого ответа с использованием динамического типа.
label.font = .init(descriptor: UIFont.preferredFont(forTextStyle: .body)
.fontDescriptor.addingAttributes([
.featureSettings: [[
UIFontDescriptor.FeatureKey.featureIdentifier: kNumberSpacingType,
.typeIdentifier: kMonospacedNumbersSelector]]]),
size: 0)
Стоит отметить, что для macOS (AppKit) он немного отличается:
NSFont(descriptor: NSFont.systemFont(ofSize: 20).fontDescriptor
.addingAttributes([.featureSettings: [[NSFontDescriptor.FeatureKey
.selectorIdentifier: kMonospacedNumbersSelector,
.typeIdentifier: kNumberSpacingType]]]), size: 0)
Немного улучшенная версия кода @Rudolf Adamkovic, которая проверяет версию iOS:
var monospacedDigitFont: UIFont {
if #available(iOS 9, *) {
let oldFontDescriptor = fontDescriptor()
let newFontDescriptor = oldFontDescriptor.monospacedDigitFontDescriptor
return UIFont(descriptor: newFontDescriptor, size: 0)
} else {
return self
}
}
Или просто используйте Helvetica. Он по-прежнему имеет одноразмерные числа и задним числом работает с более старой версией iOS.