Best way to use Dynamic Type with custom font in Swift 3 / iOS10

I tried two ways:

Method 1:

label.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.body) label.adjustsFontForContentSizeCategory = true 

This works fine, even if the preferred text size is changed in the settings, the text size changes automatically, even before I return to the application. But it only works with system font (San Francisco).

Method 2:

To use a custom font, I add the extension to UIFontDescriptor :

 //from this answer http://stackoverflow.com/a/35467158/2907715 extension UIFontDescriptor { private struct SubStruct { static var preferredFontName: String = "Avenir-medium" } static let fontSizeTable : NSDictionary = [ UIFontTextStyle.headline: [ UIContentSizeCategory.accessibilityExtraExtraExtraLarge: 23, UIContentSizeCategory.accessibilityExtraExtraLarge: 23, UIContentSizeCategory.accessibilityExtraLarge: 23, UIContentSizeCategory.accessibilityLarge: 23, UIContentSizeCategory.accessibilityMedium: 23, UIContentSizeCategory.extraExtraExtraLarge: 23, UIContentSizeCategory.extraExtraLarge: 21, UIContentSizeCategory.extraLarge: 19, UIContentSizeCategory.large: 17, UIContentSizeCategory.medium: 16, UIContentSizeCategory.small: 15, UIContentSizeCategory.extraSmall: 14 ], UIFontTextStyle.subheadline: [ UIContentSizeCategory.accessibilityExtraExtraExtraLarge: 21, UIContentSizeCategory.accessibilityExtraExtraLarge: 21, UIContentSizeCategory.accessibilityExtraLarge: 21, UIContentSizeCategory.accessibilityLarge: 21, UIContentSizeCategory.accessibilityMedium: 21, UIContentSizeCategory.extraExtraExtraLarge: 21, UIContentSizeCategory.extraExtraLarge: 19, UIContentSizeCategory.extraLarge: 17, UIContentSizeCategory.large: 15, UIContentSizeCategory.medium: 14, UIContentSizeCategory.small: 13, UIContentSizeCategory.extraSmall: 12 ], UIFontTextStyle.body: [ UIContentSizeCategory.accessibilityExtraExtraExtraLarge: 53, UIContentSizeCategory.accessibilityExtraExtraLarge: 47, UIContentSizeCategory.accessibilityExtraLarge: 40, UIContentSizeCategory.accessibilityLarge: 33, UIContentSizeCategory.accessibilityMedium: 28, UIContentSizeCategory.extraExtraExtraLarge: 23, UIContentSizeCategory.extraExtraLarge: 21, UIContentSizeCategory.extraLarge: 19, UIContentSizeCategory.large: 17, UIContentSizeCategory.medium: 16, UIContentSizeCategory.small: 15, UIContentSizeCategory.extraSmall: 14 ], UIFontTextStyle.caption1: [ UIContentSizeCategory.accessibilityExtraExtraExtraLarge: 18, UIContentSizeCategory.accessibilityExtraExtraLarge: 18, UIContentSizeCategory.accessibilityExtraLarge: 18, UIContentSizeCategory.accessibilityLarge: 18, UIContentSizeCategory.accessibilityMedium: 18, UIContentSizeCategory.extraExtraExtraLarge: 18, UIContentSizeCategory.extraExtraLarge: 16, UIContentSizeCategory.extraLarge: 14, UIContentSizeCategory.large: 12, UIContentSizeCategory.medium: 11, UIContentSizeCategory.small: 11, UIContentSizeCategory.extraSmall: 11 ], UIFontTextStyle.caption2: [ UIContentSizeCategory.accessibilityExtraExtraExtraLarge: 17, UIContentSizeCategory.accessibilityExtraExtraLarge: 17, UIContentSizeCategory.accessibilityExtraLarge: 17, UIContentSizeCategory.accessibilityLarge: 17, UIContentSizeCategory.accessibilityMedium: 17, UIContentSizeCategory.extraExtraExtraLarge: 17, UIContentSizeCategory.extraExtraLarge: 15, UIContentSizeCategory.extraLarge: 13, UIContentSizeCategory.large: 11, UIContentSizeCategory.medium: 11, UIContentSizeCategory.small: 11, UIContentSizeCategory.extraSmall: 11 ], UIFontTextStyle.footnote: [ UIContentSizeCategory.accessibilityExtraExtraExtraLarge: 19, UIContentSizeCategory.accessibilityExtraExtraLarge: 19, UIContentSizeCategory.accessibilityExtraLarge: 19, UIContentSizeCategory.accessibilityLarge: 19, UIContentSizeCategory.accessibilityMedium: 19, UIContentSizeCategory.extraExtraExtraLarge: 19, UIContentSizeCategory.extraExtraLarge: 17, UIContentSizeCategory.extraLarge: 15, UIContentSizeCategory.large: 13, UIContentSizeCategory.medium: 12, UIContentSizeCategory.small: 12, UIContentSizeCategory.extraSmall: 12 ], ] final class func preferredDescriptor(textStyle: String) -> UIFontDescriptor { let contentSize = UIApplication.shared.preferredContentSizeCategory let style = fontSizeTable[textStyle] as! NSDictionary return UIFontDescriptor(name: SubStruct.preferredFontName, size: CGFloat((style[contentSize] as! NSNumber).floatValue)) } } 

and in viewDidLoad() :

 label.font = UIFont(descriptor: UIFontDescriptor.preferredDescriptor(textStyle: UIFontTextStyle.body.rawValue), size: 0) NotificationCenter.default.addObserver(self, selector:#selector(self.userChangedTextSize(notification:)), name: NSNotification.Name.UIContentSizeCategoryDidChange, object: nil) 

here is the userChangedTextSize function:

 func userChangedTextSize(notification: NSNotification) { label.font = UIFont(descriptor: UIFontDescriptor.preferredDescriptor(textStyle: UIFontTextStyle.body.rawValue), size: 0) } 

The problem with this method is that the text size does not change until the user returns to the application, and then the user sees a change in the old text size in the new size, which is not ideal.

Can I get the best of both worlds: a custom font with a size that automatically changes in the background?

+8
source share
2 answers

The problem with this method is that the text size does not change until the user returns to the application, and then the user sees a change in the old text size in the new size, which is not ideal.

I share your thoughts that this is likely to be the best UX, but I think you are a bit overdone.

If you look at the applications included in the system (for example, Contacts), you will see that the update is not performed until the user returns to the application .

By the way, you can reorganize your code a bit for Swift 3:

 extension UIFontDescriptor { private struct SubStruct { static var preferredFontName: String = "Avenir-medium" } static let fontSizeTable: [UIFontTextStyle: [UIContentSizeCategory: CGFloat]] = [ .headline: [ .accessibilityExtraExtraExtraLarge: 23, .accessibilityExtraExtraLarge: 23, .accessibilityExtraLarge: 23, .accessibilityLarge: 23, .accessibilityMedium: 23, .extraExtraExtraLarge: 23, .extraExtraLarge: 21, .extraLarge: 19, .large: 17, .medium: 16, .small: 15, .extraSmall: 14 ], .subheadline: [ .accessibilityExtraExtraExtraLarge: 21, .accessibilityExtraExtraLarge: 21, .accessibilityExtraLarge: 21, .accessibilityLarge: 21, .accessibilityMedium: 21, .extraExtraExtraLarge: 21, .extraExtraLarge: 19, .extraLarge: 17, .large: 15, .medium: 14, .small: 13, .extraSmall: 12 ], .body: [ .accessibilityExtraExtraExtraLarge: 53, .accessibilityExtraExtraLarge: 47, .accessibilityExtraLarge: 40, .accessibilityLarge: 33, .accessibilityMedium: 28, .extraExtraExtraLarge: 23, .extraExtraLarge: 21, .extraLarge: 19, .large: 17, .medium: 16, .small: 15, .extraSmall: 14 ], .caption1: [ .accessibilityExtraExtraExtraLarge: 18, .accessibilityExtraExtraLarge: 18, .accessibilityExtraLarge: 18, .accessibilityLarge: 18, .accessibilityMedium: 18, .extraExtraExtraLarge: 18, .extraExtraLarge: 16, .extraLarge: 14, .large: 12, .medium: 11, .small: 11, .extraSmall: 11 ], .caption2: [ .accessibilityExtraExtraExtraLarge: 17, .accessibilityExtraExtraLarge: 17, .accessibilityExtraLarge: 17, .accessibilityLarge: 17, .accessibilityMedium: 17, .extraExtraExtraLarge: 17, .extraExtraLarge: 15, .extraLarge: 13, .large: 11, .medium: 11, .small: 11, .extraSmall: 11 ], .footnote: [ .accessibilityExtraExtraExtraLarge: 19, .accessibilityExtraExtraLarge: 19, .accessibilityExtraLarge: 19, .accessibilityLarge: 19, .accessibilityMedium: 19, .extraExtraExtraLarge: 19, .extraExtraLarge: 17, .extraLarge: 15, .large: 13, .medium: 12, .small: 12, .extraSmall: 12 ] ] final class func preferredDescriptor(textStyle: UIFontTextStyle) -> UIFontDescriptor { let contentSize = UIApplication.shared.preferredContentSizeCategory let style = fontSizeTable[textStyle]! return UIFontDescriptor(name: SubStruct.preferredFontName, size: style[contentSize]!) } } 

No need to specify NSDictionary or NSNumber and indirectly touch floatValue .

Thus, your site can use the following, more readable code:

 func userChangedTextSize(notification: NSNotification) { label.font = UIFont(descriptor: .preferredDescriptor(textStyle: .body), size: 0) } 

Edit: Since I'm working on the same thing now, I improved above (on a SO common solution) to make things easier.

 import UIKIt extension UIFont { private struct CustomFont { static var fontFamily = "Avenir" } /// Returns a bold version of `self` public var bolded: UIFont { return fontDescriptor.withSymbolicTraits(.traitBold) .map { UIFont(descriptor: $0, size: 0) } ?? self } /// Returns an italic version of `self` public var italicized: UIFont { return fontDescriptor.withSymbolicTraits(.traitItalic) .map { UIFont(descriptor: $0, size: 0) } ?? self } /// Returns a scaled version of `self` func scaled(scaleFactor: CGFloat) -> UIFont { let newDescriptor = fontDescriptor.withSize(fontDescriptor.pointSize * scaleFactor) return UIFont(descriptor: newDescriptor, size: 0) } class func preferredCustomFont(forTextStyle textStyle: UIFontTextStyle) -> UIFont { // we are using the UIFontDescriptor which is less expensive than creating an intermediate UIFont let systemFontDescriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: textStyle) let customFontDescriptor = UIFontDescriptor.init(fontAttributes: [ UIFontDescriptorFamilyAttribute: CustomFont.fontFamily, UIFontDescriptorSizeAttribute: systemFontDescriptor.pointSize // use the font size of the default dynamic font ]) // return font of new family with same size as the preferred system font return UIFont(descriptor: customFontDescriptor, size: 0) } } 

Using

 func userChangedTextSize(notification: NSNotification) { label.font = UIFont.preferredCustomFont(forTextStyle: .headline) // or in Bold / Italic: // label.font = UIFont.preferredCustomFont(forTextStyle: .headline).bolded // label.font = UIFont.preferredCustomFont(forTextStyle: .headline).italicized } 
+20
source

Swift 4: Custom scalable font that supports accessibility (German BITV)

 //UIFont+CustomScaledFont.swift import UIKit extension UIFont { /// Scaled and styled version of any custom Font /// /// - Parameters: /// - name: Name of the Font /// - textStyle: The text style ie Body, Title, ... /// - Returns: The scaled custom Font version with the given textStyle static func scaledFont(name:String, textStyle: UIFont.TextStyle) -> UIFont { let fontDescriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: textStyle) guard let customFont = UIFont(name: name, size: fontDescriptor.pointSize) else { fatalError("Failed to load the \(name) font.") } return UIFontMetrics.default.scaledFont(for: customFont) } } 

After successfully adding custom fonts to your project ...

Printing available font names:

 for family in UIFont.familyNames { for name in UIFont.fontNames(forFamilyName: family) { print(name) } } 

Installation example:

 myLabel.text = "My scaled custom Font" myLabel.font = UIFont.scaledFont(name: "MyCustomFontName-Bold", textStyle: .title1) myLabel.adjustsFontForContentSizeCategory = true 

Accessibility Inspector Testing (MacOS)

+1
source

Source: https://habr.com/ru/post/1263180/


All Articles