How to detect Dynamic Font size changes from iOS Settings?

Inside settings->general->text size, after changing the text size, I'd have to exit my own app to have the sizes applied to

 [UIFont preferredFontForTextStyle:..]

Is there a delegate or notification to notify my app to re-apply the new sizes?

Update: I tried the following but interestingly, the font size will apply after I BG and launch the app TWICE.

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.


[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fromBg:) name:UIApplicationDidBecomeActiveNotification object:nil];

}


 -(void) fromBg:(NSNotification *)noti{

    self.headline1.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
    self.subHeadline.font = [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
    self.body.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
    self.footnote.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
    self.caption1.font = [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
    self.caption2.font = [UIFont preferredFontForTextStyle:UIFontTextStyleCaption2];
//    [self.view layoutIfNeeded];

}

Solution 1:

You listen for the Size Change Notification on UIContentSizeCategory.

Swift 3.0: NSNotification.Name.UIContentSizeCategoryDidChange

Swift 4.0 or later: UIContentSizeCategory.didChangeNotification

Solution 2:

With Swift 5 and iOS 12, you can choose one of the three following solutions in order to solve your problem.


#1. Using UIContentSizeCategoryAdjusting's adjustsFontForContentSizeCategory property

UILabel, UITextField and UITextView conform to UIContentSizeCategoryAdjusting protocol and therefore have an instance property called adjustsFontForContentSizeCategory. adjustsFontForContentSizeCategory has the following declaration:

A Boolean value indicating whether the object automatically updates its font when the device's content size category changes.

var adjustsFontForContentSizeCategory: Bool { get set }

The UIViewController implementation below shows how to detect and react to dynamic font size changes in iOS settings with adjustsFontForContentSizeCategory:

import UIKit

class ViewController: UIViewController {

    let label = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()

        label.text = "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
        label.numberOfLines = 0
        label.font = .preferredFont(forTextStyle: UIFont.TextStyle.body)
        label.adjustsFontForContentSizeCategory = true
        view.addSubview(label)

        // Auto layout
        label.translatesAutoresizingMaskIntoConstraints = false
        let horizontalConstraint = label.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        let verticalConstraint = label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        let widthConstraint = label.widthAnchor.constraint(equalToConstant: 300)
        NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint, widthConstraint])
    }

}

#2. Using UIContentSizeCategory's didChangeNotification type property

UIContentSizeCategory has a type property called didChangeNotification. didChangeNotification has the following declaration:

Posted when the user changes the preferred content size setting.

static let didChangeNotification: NSNotification.Name

This notification is sent when the value in the preferredContentSizeCategory property changes. The userInfo dictionary of the notification contains the newValueUserInfoKey key, which reflects the new setting.

The UIViewController implementation below shows how to detect and react to dynamic font size changes in iOS settings with didChangeNotification:

import UIKit

class ViewController: UIViewController {

    let label = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()

        label.text = "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
        label.numberOfLines = 0
        label.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body)
        view.addSubview(label)

        // Register for `UIContentSizeCategory.didChangeNotification`
        NotificationCenter.default.addObserver(self, selector: #selector(preferredContentSizeChanged(_:)), name: UIContentSizeCategory.didChangeNotification, object: nil)

        // Auto layout
        label.translatesAutoresizingMaskIntoConstraints = false
        let horizontalConstraint = label.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        let verticalConstraint = label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        let widthConstraint = label.widthAnchor.constraint(equalToConstant: 300)
        NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint, widthConstraint])
    }

    @objc func preferredContentSizeChanged(_ notification: Notification) {
        label.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body)
        /* perform other operations if necessary */
    }

}

#3. Using UITraitCollection's preferredContentSizeCategory property

UITraitCollection has a property called preferredContentSizeCategory. preferredContentSizeCategory has the following declaration:

The font sizing option preferred by the user.

var preferredContentSizeCategory: UIContentSizeCategory { get }

With Dynamic Type, users can ask that apps display text using fonts that are larger or smaller than the normal font size defined by the system. For example, a user with a visual impairment might request a larger default font size to make it easier to read text. Use the value of this property to request a UIFont object that matches the user's requested size.

The UIViewController implementation below shows how to detect and react to dynamic font size changes in iOS settings with preferredContentSizeCategory:

import UIKit

class ViewController: UIViewController {

    let label = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()

        label.text = "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
        label.numberOfLines = 0
        label.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body)
        view.addSubview(label)

        // Auto layout
        label.translatesAutoresizingMaskIntoConstraints = false
        let horizontalConstraint = label.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        let verticalConstraint = label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        let widthConstraint = label.widthAnchor.constraint(equalToConstant: 300)
        NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint, widthConstraint])
    }

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

        if previousTraitCollection?.preferredContentSizeCategory != traitCollection.preferredContentSizeCategory {
            self.label.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body)
            /* perform other operations if necessary */
        }
    }

}

Sources:

  • useyourloaf.com / Auto Adjusting Fonts for Dynamic Type