How to honor Dynamic Type Accessibility Sizes with a custom font in an iOS storyboard
Asked Answered
E

3

17

How can I use the dynamic type text style "Title 1" and set the font face to the built-in font Chalkboard SE for a UILabel in a storyboard?

I need to honor the Dynamic Type size in iOS (Apple has been encouraging this since iOS 7?) I also need to use the built-in font Chalkboard SE, which is not used by default for the "text styles" fonts. I am currently using a custom font as shown in the image, but need the font to change size in accordance with the user's Dynamic Type/Accessibility Sizes preference just as all the Text Styles fonts do. The best Text Styles option is Title 1, but the font/typeface is unacceptable.

Font menu in Xcode. Custom checked and Title 1 highlighted

Eous answered 12/2, 2016 at 5:51 Comment(2)
Can't be done via storyboard alone. See How to use a custom font with dynamic text sizes in iOS7 for programmatic answers.Phage
That's a truly incredibly large amount of code for one UILabel. It's also in Objective-C so I'll have to translate it for this project. Is there really no practical way to use Dynamic Type with a custom font?Eous
P
27

Although you can't specify both a custom font and a preferred text style via Storyboard, it's not difficult to programmatically specify a dynamic type size for your custom font:

Swift:

let pointSize  = UIFontDescriptor.preferredFontDescriptorWithTextStyle(UIFont‌​TextStyleTitle1).poi‌​ntSize
let customFont = UIFont(name: "Chalkboard SE", size: pointSize)

When you receive a UIContentSizeCategoryDidChangeNotification, use the same code to update your label's font.

Obj C:

 CGFloat pointSize = [[UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleHeadline] pointSize];
 [titleLabel setFont:[UIFont fontWithName:@"Marker Felt" size:pointSize]];
Phage answered 12/2, 2016 at 7:55 Comment(5)
Any way to make this work for iOS 8 or 7? UIFontTextStyleTitle1 is only available in iOS 9Eous
I used Title1 because that's what you had specified in your question. If you need to support an older OS, you would have to substitute one of the other text styles.Phage
I understand that that's why you used Title1. I've done just as you suggest and set the font size to that of UIFontTextStyleHeadline * 1.6 for the else of if #available(iOS 9.0, *)Eous
let pointSize = UIFontDescriptor.preferredFontDescriptorWithTextStyle(UIFontTextStyleTitle1).pointSize should be preferred as initialising a UIFont is more expensive.Alloy
let pointSize = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .title1).pointSize for swift 3 +Popeyed
M
2

We have to add all the extension of related UIFramework for which we have to use dynamic font size of custom font in iOS-11+ such as UILabel, UIButton, UITextField, UITextView. Code is given in swift 4.2. We just need to use these custom classes instead of native ios classes to see effect of dynamic fonts in app.

Here first is Label:

class AppLabel: UILabel {

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
    if #available(iOS 11.0, *)  {

        self.font = UIFontMetrics.default.scaledFont(for: self.font)
        self.adjustsFontForContentSizeCategory = true
        // Fallback on earlier versions
    } else {
        // Fallback on earlier versions
    }
  }

}

Here 2nd is UIButton:

class AppButton: UIButton {

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
    if #available(iOS 11.0, *) {

        self.titleLabel?.font = UIFontMetrics.default.scaledFont(for: self.titleLabel?.font ?? UIFont())
        self.titleLabel?.adjustsFontForContentSizeCategory = true
    } else {
        // Fallback on earlier versions 
    }
  }
}

Here 3rd is UITextField:

class AppTextField: UITextField {
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
    if #available(iOS 11.0, *) {

        self.font = UIFontMetrics.default.scaledFont(for: self.font ?? UIFont())
        self.adjustsFontForContentSizeCategory = true
    } else {
        // Fallback on earlier versions
    }
  }
}

Last one in UITextView:

class AppTextView: UITextView {

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
    if #available(iOS 11.0, *) {

        self.font = UIFontMetrics.default.scaledFont(for: self.font ?? UIFont())
        self.adjustsFontForContentSizeCategory = true
    } else {
        // Fallback on earlier versions
    }
  }
}
Madore answered 30/11, 2018 at 13:57 Comment(1)
You just need to add above custom classes in your project and change the class in interface builder for UILabel to AppLabel. Also update classes for UITextView and UITextField to AppTextView and AppTextField respectively. Please let me know if you face any issue.Madore
E
0

Try my solution without need to listening for NSNotification.Name.UIContentSizeCategoryDidChange.

Simply override traitCollectionDidChange on any UIView or UITableViewCell and update/calulate the fontsize with the similar TextStyle.

You can simply test it with any simulated Device and the Accessibility Inspector on MacOS.

 override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
       super.traitCollectionDidChange(previousTraitCollection)

        let pointSize = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .title1).pointSize
        titleLabel.font = titleLabel.font.withSize(pointSize)
    }
Elaterid answered 14/5, 2018 at 16:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.