UIFont monospaced digits + small caps

628 views Asked by At

I'm trying to create a UIFont with the following attributes:

  • Upper Case Small Caps
  • Lower Case Small Caps
  • Monospaced Digits

I'm using the system font (San Francisco), which does supports all theses features.
As far as I know, the only way to do it is to use multiple UIFontDescriptor.

Here is the code I'm using:

extension UIFont {

    var withSmallCaps: UIFont {
        let upperCaseFeature = [
            UIFontDescriptor.FeatureKey.featureIdentifier : kUpperCaseType,
            UIFontDescriptor.FeatureKey.typeIdentifier : kUpperCaseSmallCapsSelector
        ]
        let lowerCaseFeature = [
            UIFontDescriptor.FeatureKey.featureIdentifier : kLowerCaseType,
            UIFontDescriptor.FeatureKey.typeIdentifier : kLowerCaseSmallCapsSelector
        ]
        let features = [upperCaseFeature, lowerCaseFeature]
        let smallCapsDescriptor = self.fontDescriptor.addingAttributes([UIFontDescriptor.AttributeName.featureSettings : features])

        return UIFont(descriptor: smallCapsDescriptor, size: pointSize)
    }

    var withMonospacedDigits: UIFont {
        let monospacedDigitsFeature = [
            UIFontDescriptor.FeatureKey.featureIdentifier : kNumberSpacingType,
            UIFontDescriptor.FeatureKey.typeIdentifier : kMonospacedNumbersSelector
        ]
        let monospacedDigitsDescriptor = self.fontDescriptor.addingAttributes([UIFontDescriptor.AttributeName.featureSettings : [monospacedDigitsFeature]])

        return UIFont(descriptor: monospacedDigitsDescriptor, size: pointSize)
    }
}

I should be able to obtain a font with all the characteristics mentioned earlier with this line of code:

let font = UIFont.systemFont(ofSize: 16, weight: .regular).withSmallCaps.withMonospacedDigits
// OR
let font = UIFont.monospacedDigitSystemFont(ofSize: 16, weight: .regular).withSmallCaps

But for some reasons, it does not work. I can't get the font to have monospaced digits while having small caps at the same time.

What am I doing wrong?

2

There are 2 answers

1
ielyamani On

Have a look at the reference document for more details. I would suggest using attributed strings with small caps for all glyphs but numbers, and another font for monospaced digits. here is some sample code:

let monoSpacedDigits = UIFont.systemFont(ofSize: 13, weight: .medium).withMonospacedDigits
let smallCaps = UIFont.systemFont(ofSize: 16, weight: .regular).withSmallCaps



let attributedString = NSMutableAttributedString(string: """
H3ll0 7here
1111111111
2222222222
3333333333
4444444444
5555555555
6666666666
7777777777
8888888888
9999999999
0000000000
""", attributes: [NSAttributedStringKey.font : smallCaps])

do {
    let regex = try NSRegularExpression(pattern: "[0-9]")
    let range = NSRange(0..<attributedString.string.utf16.count)
    let matches = regex.matches(in: attributedString.string, range: range)
    for match in matches.reversed() {
        attributedString.addAttribute(NSAttributedStringKey.font, value: monoSpacedDigits, range: match.range)
    }
} catch {
    // Do error processing here...
    print(error)
}

myLabel.attributedText = attributedString

I've used a size of 13 and a medium weight to make the monospaced digits look as similar as possible to the small caps.

4
Vin Gazoil On

I figured out why it does not work thanks to the reference document linked by @Carpsen90.

It seems like the Number Spacing feature is exclusive.

As stated in the document:

Features are classified as "exclusive" and "nonexclusive." This indicates whether several different selectors within a given feature type may be selected at once. Thus, it is possible to have both common and rare ligatures turned on, whereas it is impossible to display a given fraction as simultaneously vertical and diagonal.

So having both monospaced digits + small caps features at the same time is impossible.


EDIT:

I misread the document. Selectors of that feature are exclusive. But not the whole feature. So it should be possible.