2

Summary

macOS 14 Sonoma adds the "Text Size" setting under Settings -> Accessibility -> Display. This is similar to the "Text Size" setting that has been in iOS for quite some time.

Many Apple apps automatically adjust the size of their text based on this setting. This includes Xcode, Settings, Finder, Mail, Notes, and others. Apps like Xcode and Settings only change the text in sidebars while the other apps change text throughout the app.

Question

My question is how to respond to this setting in my own macOS app?

Research

I've searched the AppKit documentation. I've searched WWDC videos. I've searched the Apple developer forums. There's no mention of how this is done that I have seen.

Here on SO I've seen:

Experiments

I've created a native macOS app using AppKit. I've added an NSTextField and an NSTextView and set the fonts to a text style such as "Title 1". Changing the setting does not automatically change the displayed font size in the app.

I've created a SwiftUI app and added a native macOS build. I setup a ContentView with some Text using Text("This is some text").font(.title). Changing the setting does not automatically change the displayed font size in the app.

I have an iOS app built for macOS using Mac Catalyst. The iOS version of the app automatically adjusts its text when the "Text Size" setting is changed in the iOS Settings app. The macOS version of the app does not change at all when the "Text Size" setting is changed in the macOS Settings app.

In the native AppKit app I tried the following code in the AppDelegate:

NSWorkspace.shared.notificationCenter.addObserver(forName: nil, object: nil, queue: nil) { note in
    print(note)
}
NotificationCenter.default.addObserver(forName: nil, object: nil, queue: nil) { note in
    print(note)
}

Neither of these generated any output when the "Text Size" setting was changed. Under iOS/UIKit, you get a UIContentSizeCategory.didChangeNotification notification.

As a shot in the dark I setup the AppKit app with my own custom NSApplication subclass and added the following:

class Application: NSApplication {
    override func sendEvent(_ event: NSEvent) {
        print("event \(event)")
        super.sendEvent(event)
    }

    override func sendAction(_ action: Selector, to target: Any?, from sender: Any?) -> Bool {
        print("action: \(action)")
        return super.sendAction(action, to: target, from: sender)
    }
}

Neither of these produced output when the "Text Size" setting was changed.

Summary

I'm at a loss at this point. What code is needed to allow a macOS app to respond to changes to the "Text Size" setting? My immediate need is with an iOS/UIKit app built for macOS with Mac Catalyst but I'm fine with solutions in native AppKit or even SwiftUI. Code can be in either Swift or Objective-C.

3
  • Did you find any non-Apple apps responding to this? Nothing related to this has been mentioned in the release notes. It could very well be that the API for this is not public yet. Commented Feb 4, 2024 at 20:05
  • @Sweeper I only have two non-Apple apps on my Mac including Firefox and neither seem to respond to the Text Size setting. Obviously that is a near worthless sampling of apps. Commented Feb 4, 2024 at 20:25
  • Any update on that macOS Sequoia? Commented Sep 24, 2024 at 2:12

1 Answer 1

2

Getting Text Size with defaults

Text size are stored in com.apple.universalaccess.plist as FontSizeCategory:

$ defaults read com.apple.universalaccess FontSizeCategory

# {
#     "com.apple.MobileSMS" = UseGlobal;
#     "com.apple.Notes" = UseGlobal;
#     "com.apple.finder" = UseGlobal;
#     "com.apple.iBooksX" = UseGlobal;
#     "com.apple.iCal" = UseGlobal;
#     "com.apple.mail" = UseGlobal;
#     "com.apple.news" = UseGlobal;
#     "com.apple.stocks" = UseGlobal;
#     "com.apple.weather" = UseGlobal;
#     global = DEFAULT;
#     version = "3.0";
# }

global is what we're interested in.

Here are a list of possible values and corresponding options in the Settings app:

Value Text Size Sidebar Icon Size
XXXS 9pt Small
XXS 10pt Small
XS 11pt Small
S 12pt Medium
DEFAULT Default (11pt) Medium
M 13pt Medium
L 14pt Medium
XL 15pt Large
XXL 16pt Large
XXXL 17pt Large
AX1 20pt Large
AX2 24pt Large
AX3 29pt Large
AX4 35pt Large
AX5 42pt Large

Observing Text Size Changes in Code

To observe text size changes:

  1. Create a UserDefaults object with suite name com.apple.universalaccess. Make sure you keep a strong reference to it, or you're not getting KVO callbacks.

  2. Observe changes for key FontSizeCategory with KVO.

extension UserDefaults {
    // I'm using `observe(_:options:changeHandler:)` below and it only accept 
    // a `KeyPath` key path, so this is required for this use case. 
    // Notice that the property name is the same as the key `FontSizeCategory`, 
    // because the property name is used for KVO key.
    @objc var FontSizeCategory: [String: Any]? {
        dictionary(forKey: "FontSizeCategory")
    }
}

let universalAccessDefaults = UserDefaults(suiteName: "com.apple.universalaccess")!
var fontSizeCategoryObservation: NSKeyValueObservation?

fontSizeCategoryObservation = universalAccessDefaults.observe(\.FontSizeCategory, options: .new) {  _, changes in
    guard 
      let newValue = changes.newValue.flatMap(\.self), 
      let global = newValue["global"] as? String 
    else { return }

    /* Update your UI for text size change */
}

Some said that this requires a temporary exception entitlement and is usually not allowed for App Store.

Getting Scaled Font Size for Text Size

Unfortunately, we don't have dynamic type in macOS APIs yet. Methods like NSFont.preferredFont(forTextStyle:) always return a fixed font size.

For now, we would need to maintain a list of font size our own, migrated from iOS. This is a lot of works, and I barely know anything about typography, so I'm leaving this to you.

Here are some links that might be useful:

Sign up to request clarification or add additional context in comments.

3 Comments

This is some good info, thanks. I'll have to see if Apple will allow my app to access those user defaults.
You stated: "Unfortunately, we don't have dynamic type in macOS APIs yet." - my app is an iOS app that runs on macOS via Mac Catalyst. Under macOS 12 - 14 my app gets fully working dynamic type using UIFont.preferredFont(forTextStyle:). Unfortunately, this no longer works under macOS 15. I don't understand why Apple would allow 1st party apps to properly handle the user's chosen system text size on macOS, but prevent 3rd party apps from doing so. At the same time they make it so easy in iOS.
I don't understand this too. Dynamic Type on Mac is like a bug to them. I used to believed that it's because we can change the resolution on Mac so maybe we don't need Dynamic Type, but now they come up with Text Size. 😕

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.