Wednesday, July 4, 2018

How do I get a monospace font that respects acessibility settings

Leave a Comment
let bodyFontDescriptor = UIFontDescriptor     .preferredFontDescriptor(withTextStyle: UIFontTextStyle.body) let bodyMonospacedFontDescriptor = bodyFontDescriptor.addingAttributes(     [         UIFontDescriptorFeatureSettingsAttribute: [             [                 UIFontFeatureTypeIdentifierKey: kTextSpacingType,                 UIFontFeatureSelectorIdentifierKey: kMonospacedTextSelector             ]         ]     ]) let bodyMonospacedFont = UIFont(descriptor: bodyMonospacedFontDescriptor, size: 0.0) textview.font = bodyMonospacedFont 

This produces text with characters of variable width. I need to get a monospace font without hardcoding courier new and fixed size. Deployment target is ios 9.0

1 Answers

Answers 1

Here is an extension to UIFontDescriptor that returns a preferred monospaced font descriptor for a given text style. There is no simple way to get a fully monospaced font using UIFont or UIFontDescriptor. This solution attempts to find a good monospaced font and falls back to Courier if needed.

extension UIFontDescriptor {     static let monoDescriptor: UIFontDescriptor = {         // Attempt to find a good monospaced, non-bold, non-italic font         for family in UIFont.familyNames {             for name in UIFont.fontNames(forFamilyName: family) {                 let f = UIFont(name: name, size: 12)!                 let fd = f.fontDescriptor                 let st = fd.symbolicTraits                 if st.contains(.traitMonoSpace) && !st.contains(.traitBold) && !st.contains(.traitItalic) && !st.contains(.traitExpanded) && !st.contains(.traitCondensed) {                     return fd                 }             }         }          return UIFontDescriptor(name: "Courier", size: 0) // fallback     }()      class func preferredMonoFontDescriptor(withTextStyle style: UIFontTextStyle) -> UIFontDescriptor {         // Use the following line if you need a fully monospaced font         let monoDescriptor = UIFontDescriptor.monoDescriptor          // Use the following two lines if you only need monospaced digits in the font         //let monoDigitFont = UIFont.monospacedDigitSystemFont(ofSize: 0, weight: .regular)         //let monoDescriptor = monoDigitFont.fontDescriptor          // Get the non-monospaced preferred font         let defaultFontDescriptor = preferredFontDescriptor(withTextStyle: style)         // Remove any attributes that specify a font family or name and remove the usage         // This will leave other attributes such as size and weight, etc.         var fontAttrs = defaultFontDescriptor.fontAttributes         fontAttrs.removeValue(forKey: .family)         fontAttrs.removeValue(forKey: .name)         fontAttrs.removeValue(forKey: .init(rawValue: "NSCTFontUIUsageAttribute"))         let monospacedFontDescriptor = monoDescriptor.addingAttributes(fontAttrs)          return monospacedFontDescriptor.withSymbolicTraits(defaultFontDescriptor.symbolicTraits) ?? monospacedFontDescriptor     } } 

Note the comments about whether you need a font that is fully monospaced or a font that just has monospaced digits. Comment/Uncomment those lines to suit your specific needs.

Sample usage:

let bodyMonospacedFont = UIFont(descriptor: .preferredMonoFontDescriptor(withTextStyle: .body), size: 0) textview.font = bodyMonospacedFont 

The following is some test code to confirm that the results of preferredMonoFontDescriptor(withTextStyle:) works properly for all styles:

let textStyles: [UIFontTextStyle] = [ .body, .callout, .caption1, .caption2, .footnote, .headline, .subheadline, .largeTitle, .title1, .title2, .title3 ] for style in textStyles {     let nfont = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: style), size: 0)     let mfont = UIFont(descriptor: .preferredMonoFontDescriptor(withTextStyle: style), size: 0)     print(style)     print(nfont)     print(mfont) } 

If you compare each pair of results, they have the same size, weight, and style, just a different font.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment