SFPro Font Features


TODO: Intro WWDC Session

Making sense of Feature Keys

TODO: Complain about keys Font Feature Registry

extension UIFont {
	struct Feature: Hashable {
		private let type: Int
		private let selector: Int
		var setting: [UIFontDescriptor.FeatureKey : Int] { 
			[.featureIdentifier: type, .typeIdentifier: selector]
		}
	}
}

Useful features

extension UIFont {
	static let straightSixAndNine = Feature(type: kStylisticAlternativesType, selector: kStylisticAltOneOnSelector)
	static let openFour = Feature(type: kStylisticAlternativesType, selector: kStylisticAltTwoOnSelector)
	static let alignedColon = Feature(type: kStylisticAlternativesType, selector: kStylisticAltThreeOnSelector)
	static let highLegibility = Feature(type: kStylisticAlternativesType, selector: kStylisticAltSixOnSelector)
	static let oneStoreyA = Feature(type: kStylisticAlternativesType, selector: kStylisticAltSixOnSelector)
	static let monospacedNumbers = Feature(type: kNumberSpacingType, selector: kMonospacedNumbersSelector)
	static let diagonalFractions = Feature(type: kFractionsType, selector: kDiagonalFractionsSelector)
	static let loverCaseSmallCaps = Feature(type: kLowerCaseType, selector: kLowerCaseSmallCapsSelector)
	static let upperCaseSmallCaps = Feature(type: kUpperCaseType, selector: kUpperCaseSmallCapsSelector)
}

Caching

TODO: To improve performance caching can be used. Can be a simple dictionary.

extension UIFont {
	private static var cache = [Key: UIFont]()
	
	private struct Key: Hashable {
		let size: CGFloat
		let weight: Weight
		let features: [Feature]
		let italic: Bool
	}
}

Usage

TODO: Construct font using the descriptor or retrieve from cache.

extension UIFont {
	/// Watch [WWDC Session](https://developer.apple.com/videos/play/wwdc2015/804/).
	/// [Font Feature Registry](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html)
	/// - Parameters:
	///   - size: The size (in points) to which the font is scaled. This value must be greater than 0.0.
	///   - weight: The weight of the font, specified as a font weight constant.
	///   - features: Font features supported by SFPro
	///   - italic: Italic variant of the font.
	static func systemFont(
		ofSize size: CGFloat,
		weight: Weight = .regular,
		features: [Feature] = [],
		italic: Bool = false
	) -> UIFont {
		let key = Key(size: size, weight: weight, features: features, italic: italic)
		if let font = cache[key] { return font }
		let descriptor = UIFont.systemFont(ofSize: size, weight: weight).fontDescriptor
		let font = UIFont(
			descriptor: descriptor
				.addingAttributes([.featureSettings: features.map { $0.setting }])
				.withSymbolicTraits(descriptor
					.symbolicTraits
					.union(italic ? .traitExpanded : .init(rawValue: 0))
				) ?? descriptor,
			size: size
		)
		cache[key] = font
		return font
	}
}

Sketch

sketch