< Introduction to accessibility with SwiftUI | How to detect the Reduce Motion accessibility setting > |
Updated for Xcode 12.0
Updated for iOS 14
If you’re using iOS 14 or later you’ll find your custom fonts scale automatically with no further work from you. However, if you want your fonts to scale relative to a specific Dynamic Type font, you should use the relativeTo
parameter like this:
Text("Scaling")
.font(.custom("Georgia", size: 24, relativeTo: .headline))
That will start the font at 24pt, but it will scale up and down relative to the Headline Dynamic Type font.
If you’re stuck on iOS 13 then continue reading below…
SwiftUI comes with support for all of Dynamic Type’s font sizes, all set using the .font()
modifier. However, if you ask for a specific font and size, you’ll find your text no longer scales up or down automatically according to the user’s Dynamic Type settings – it remains fixed.
To work around this we need to create a custom ViewModifier
that can scale up our font size based on the current accessibility setting, and also detect when that setting changes.
I’m going to give you the code first, then walk through how it works and why:
@available(iOS 13, macCatalyst 13, tvOS 13, watchOS 6, *)
struct ScaledFont: ViewModifier {
@Environment(\.sizeCategory) var sizeCategory
var name: String
var size: CGFloat
func body(content: Content) -> some View {
let scaledSize = UIFontMetrics.default.scaledValue(for: size)
return content.font(.custom(name, size: scaledSize))
}
}
@available(iOS 13, macCatalyst 13, tvOS 13, watchOS 6, *)
extension View {
func scaledFont(name: String, size: CGFloat) -> some View {
return self.modifier(ScaledFont(name: name, size: size))
}
}
That’s all the code required to get custom fonts working with Dynamic Type. As an example of using it, here’s a list with two text views, one using the built-in font and one with a scalable Georgia font:
struct ContentView: View {
var body: some View {
List {
Text("Hello World")
Text("Hello World")
.scaledFont(name: "Georgia", size: 12)
}
}
}
Now you’ve seen how it works, let’s look at why it works.
First, we have this custom view modifier:
struct ScaledFont: ViewModifier {
@Environment(\.sizeCategory) var sizeCategory
var name: String
var size: CGFloat
func body(content: Content) -> some View {
let scaledSize = UIFontMetrics.default.scaledValue(for: size)
return content.font(.custom(name, size: scaledSize))
}
}
That accepts a name and size for our font, then uses UIFontMetrics
to scale up the requested font to whatever matches the user’s current device setting, and send it back.
We then wrap that inside an extension on View
to make it easier to use:
@available(iOS 13, macCatalyst 13, tvOS 13, watchOS 6, *)
extension View {
func scaledFont(name: String, size: CGFloat) -> some View {
return self.modifier(ScaledFont(name: name, size: size))
}
}
All that does is wrap up the call to our custom font modifier so that it looks nicer in our views – it means we write .scaledFont(name: "Georgia", size: 12)
to use it, rather than .modifier(ScaledFont(name: "Georgia", size: 12))
.
Now, you might wonder why we need the custom view modifier if all we do is pass on the data. Well, the clue lies in this line in our view modifier:
@Environment(\.sizeCategory) var sizeCategory
That asks the system to provide the current size category from the environment, which determines what level Dynamic Type is set to. The trick is that we don’t actually use it – we don’t care what the Dynamic Type setting is, but by asking the system to update us when it changes our UIFontMetrics
code will be run at the same time, causing our font to scale correctly.
Tip: The UIFontMetrics
class is not available on macOS, which is why I’ve added the @available
markers.
SPONSORED Would you describe yourself as knowledgeable, but struggling when you have to come up with your own code? Fernando Olivares has a new book containing iOS rules you can immediately apply to your coding habits to see dramatic improvements, while also teaching applied programming fundamentals seen in refactored code from published apps.
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.