跳转至
Structure

ScaledMetric

Scale a float given the system font size.

Declaration

@propertyWrapper struct ScaledMetric<Value> : DynamicProperty where Value : BinaryFloatingPoint

Overview

ScaledMetric is a property wrapper that scales a number conforming to the BinaryFloatingPoint protocol in accordance with the user's Dynamic Type setting.

The purpose of ScaledMetric is to allow your app views – not just your fonts – to scale given the user's Dynamic Type settings.

Scaled Metric can be declared in two ways:

  1. From a CGFloat. For example, @ScaledMetric var scaledHeight: CGFloat = 32
  2. From a Font.TextStyle. For example, @ScaledMetric(relativeTo: .title) var scaledHeight: CGFloat = 32

Declaring ScaledMetric from a float

This code scaled a RoundedRectangle view given the user's font size. Notice that both scaledHeight and defaultHeight are CGFloats set to 32.

Given default font settings, this code renders as expected.

Scaled Metric

However, when the user adjusts the font size:

Large Font

The rectangle view changes accordingly while the code stays constant:

Large View

Similarly, when the user adjusts to a smaller font size:

Small Font

The RoundedRectangle shrinks while the code still has note changed:

Small View

struct ExampleView: View {
    @ScaledMetric var scaledHeight: CGFloat = 32
    var defaultHeight: CGFloat = 32

    var body: some View {
        VStack {
            Text("Scaled Metric Height \(scaledHeight)")
            RoundedRectangle(cornerRadius: 10)
                .frame(height: scaledHeight)

            Text("Default Height \(defaultHeight)")
            RoundedRectangle(cornerRadius: 10)
                .frame(height: defaultHeight)
        }
        .font(.caption)
        .foregroundColor(.pink)
        .padding()
    }
}

Declaring a ScaledMetric from a font

Font sizes do not adjust as a continuous function given the Dynamic Type setting. Instead, explicit integer font sizes are declared given the different Dynamic Type options. Find these font sizes here.

Now, assume an element in your view is meant to correspond to an exact text size regardless of a user's Dynamic Type settings. This behavior can be achieved by initializing ScaledMetric relative to a TextStyle.

Given default font settings, this code renders as expected.

Scaled Metric

struct ExampleView: View {
    @ScaledMetric(relativeTo: .title) var scaledHeight: CGFloat = 32
    var defaultHeight: CGFloat = 32

    var body: some View {
        HStack {
            Text("My Title")
                .font(.title)
            RoundedRectangle(cornerRadius: 10)
                .frame(height: scaledHeight)
                .foregroundColor(.pink)
        }

        Text("\(scaledHeight)")
    }
}
However, when the user adjusts the font size:

Large Font

The view changes accordingly while the code stays constant.

  • Note: The scaled float generated from this font initialier is 36.00, whereas the scaled float generated from the CGFloat was 42.00. Large View
struct ExampleView: View {
    @ScaledMetric var scaledHeight: CGFloat = 32
    var defaultHeight: CGFloat = 32

    var body: some View {
        VStack {
            Text("Scaled Metric Height \(scaledHeight)")
            RoundedRectangle(cornerRadius: 10)
                .frame(height: scaledHeight)

            Text("Default Height \(defaultHeight)")
            RoundedRectangle(cornerRadius: 10)
                .frame(height: defaultHeight)
        }
        .font(.caption)
        .foregroundColor(.pink)
        .padding()
    }
}

struct ExampleView: View {
    @ScaledMetric var scaledHeight: CGFloat = 32
    var defaultHeight: CGFloat = 32

    var body: some View {
        VStack {
            Text("Scaled Metric Height \(scaledHeight)")
            RoundedRectangle(cornerRadius: 10)
                .frame(height: scaledHeight)

            Text("Default Height \(defaultHeight)")
            RoundedRectangle(cornerRadius: 10)
                .frame(height: defaultHeight)
        }
        .font(.caption)
        .foregroundColor(.pink)
        .padding()
    }
}
Similarly, when the user adjusts to a smaller font size:

Small Font

The RoundedRectangle shrinks while the code still has note changed.

  • Note: The scaled float generated from this font initialier is 31.00, whereas the scaled float generated from the CGFloat was 27.50. Small View
struct ExampleView: View {
    @ScaledMetric var scaledHeight: CGFloat = 32
    var defaultHeight: CGFloat = 32

    var body: some View {
        VStack {
            Text("Scaled Metric Height \(scaledHeight)")
            RoundedRectangle(cornerRadius: 10)
                .frame(height: scaledHeight)

            Text("Default Height \(defaultHeight)")
            RoundedRectangle(cornerRadius: 10)
                .frame(height: defaultHeight)
        }
        .font(.caption)
        .foregroundColor(.pink)
        .padding()
    }
}
struct ExampleView: View {
    @ScaledMetric var scaledHeight: CGFloat = 32
    var defaultHeight: CGFloat = 32

    var body: some View {
        VStack {
            Text("Scaled Metric Height \(scaledHeight)")
            RoundedRectangle(cornerRadius: 10)
                .frame(height: scaledHeight)

            Text("Default Height \(defaultHeight)")
            RoundedRectangle(cornerRadius: 10)
                .frame(height: defaultHeight)
        }
        .font(.caption)
        .foregroundColor(.pink)
        .padding()
    }
}

More

By default, font sizes scale with Dynamic Types. However, if your app declares a custom font size, it will not scale by default.

For example, the follow view will not scale when the user's font is set to large:

struct ContentView: View {
    var body: some View {
        Text("Banana 🍌")
            .font(.system(size: 34, weight: .bold, design: .rounded))
    }
}
However, by setting a font size to a scaled value, the text will scale accordingly:

Font Example

struct ContentView: View {
    @ScaledMetric(relativeTo: .largeTitle) var dynamicFontSize: CGFloat = 34

    var body: some View {
        Text("Banana 🍌")
            .font(.system(size: dynamicFontSize, weight: .bold, design: .rounded))
    }
}
Learn more about Dynamic Type on Apple's "Human Interface Guidlines" typography page.

Availability

iOS 14.0+

macOS 11.0+

tvOS 14.0+

watchOS 7.0+

Topics


Instance Property

wrappedValue The value scaled based on the current environment.


Initializer

init(wrappedValue:) Creates the scaled metric with an unscaled value using the default scaling.

init(wrappedValue:relativeTo:) Creates the scaled metric with an unscaled value and a text style to scale relative to.