Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2024 SwiftUI & UI FrameworksAccessibility & Inclusion

WWDC24 · 16 min · SwiftUI & UI Frameworks / Accessibility & Inclusion

Get started with Dynamic Type

Dynamic Type lets people choose their preferred text size across the system and all of their apps. To help you get started supporting Dynamic Type, we’ll cover the fundamentals: How it works, how to find issues with scaling text in your app, and how to take practical steps using SwiftUI and UIKit to create a great Dynamic Type experience. We’ll also show how you can best use the Large Content Viewer to make navigation controls accessible to everyone.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

Code shown on screen · 11 snippets

Built-in text styles with SwiftUI swift · at 3:53 ↗
// Use built-in text styles with SwiftUI

import SwiftUI

struct ContentView: View {

    var body: some View {
        Text("Hello, World!")
            .font(.title)
    }

}
Built-in text styles in UIKit swift · at 4:06 ↗
// Built-in text styles in UIKit

import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let label = UILabel(frame: .zero)
        setupConstraints()
        label.text = "Hello, World!"
        label.adjustsFontForContentSizeCategory = true
        label.font = .preferredFont(forTextStyle: .title1)
        label.numberOfLines = 0
        
        self.view.addSubview(label)
    }
}
Dynamic layout in SwiftUI swift · at 7:20 ↗
// Dynamic layout in SwiftUI

import SwiftUI

struct FigureCell: View {
    @Environment(\.dynamicTypeSize) 
    private var dynamicTypeSize: DynamicTypeSize
    
    var dynamicLayout: AnyLayout { 
        dynamicTypeSize.isAccessibilitySize ?
        AnyLayout(HStackLayout()) : AnyLayout(VStackLayout())
    }
    
    let systemImageName: String
    let imageTitle: String
    
    var body: some View {
        dynamicLayout {
            FigureImage(systemImageName: systemImageName)
            FigureTitle(imageTitle: imageTitle)
        }
    }
}
Dynamic layout in SwiftUI swift · at 7:52 ↗
// Dynamic layout in SwiftUI

import SwiftUI

struct FigureContentView: View {
    @Environment(\.dynamicTypeSize) 
    private var dynamicTypeSize: DynamicTypeSize
    
    var dynamicLayout: AnyLayout {
        dynamicTypeSize.isAccessibilitySize ?
        AnyLayout(VStackLayout(alignment: .leading)) : AnyLayout(HStackLayout(alignment: .top))
    }
    
    var body: some View {
        dynamicLayout {
            FigureCell(systemImageName: "figure.stand", imageTitle: "Standing Figure")
            FigureCell(systemImageName: "figure.wave", imageTitle: "Waving Figure")
            FigureCell(systemImageName: "figure.walk", imageTitle: "Walking Figure")
            FigureCell(systemImageName: "figure.roll", imageTitle: "Rolling Figure")
        }
    }
}
Dynamic layout in UIKit swift · at 8:20 ↗
// Dynamic layout in UIKit

import UIKit

class ViewController: UIViewController {
    private var mainStackView: UIStackView = UIStackView()
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        NotificationCenter.default.addObserver(self, selector: #selector(textSizeDidChange(_:)), name: UIContentSizeCategory.didChangeNotification, object: nil)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupStackView()
    }

    @objc private func textSizeDidChange(_ notification: Notification?) {
        let isAccessibilityCategory = self.traitCollection.preferredContentSizeCategory.isAccessibilityCategory
        mainStackView.axis = isAccessibilityCategory ? .vertical : .horizontal
        setupConstraints()
    }
}
Scale inline images with SwiftUI swift · at 10:12 ↗
// Inline images in SwiftUI

import SwiftUI

struct ContentView: View {

    var body: some View {
        List {
            FigureListCell(figureName: "Standing Figure",
                systemImage: "figure.stand")
            FigureListCell(figureName: "Rolling Figure",
                  systemImage: "figure.roll")
            FigureListCell(figureName: "Waving Figure",
                systemImage: "figure.wave")
            FigureListCell(figureName: "Walking Figure",
                systemImage: "figure.walk")
        }
    }

}
Scale inline images with UIKit swift · at 10:30 ↗
// Inline images in UIKit

func attributedStringWithImage(systemImageName: String, imageTitle: String) ->  
    NSAttributedString {
    let attachment = NSTextAttachment()
    attachment.image = UIImage(systemName: systemImageName)
    
    let attachmentAttributedString = NSMutableAttributedString(attachment: attachment)
    attachmentAttributedString.append(NSAttributedString(string: imageTitle))
    
    return attachmentAttributedString
}
Scale images in SwiftUI swift · at 11:05 ↗
// Scaling images in SwiftUI

import SwiftUI

struct ContentView: View {
    @ScaledMetric var imageWidth = 125.0
    var body: some View {
        VStack {
            Image("Spatula")
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(width: imageWidth)
            Text("Grill Party!")
                .frame(alignment: .center)
        }
    }
}
Scale symbols with UIKit swift · at 11:38 ↗
// Symbol configuration in UIKit

import UIKit

func imageWithBodyConfiguration(systemImageName: String) -> UIImage? {
  let imageConfiguration = UIImage.SymbolConfiguration(textStyle: .body)
  let configuredImage = UIImage(systemName: systemImageName, withConfiguration: imageConfiguration)
  return configuredImage
}
Add large content viewer support with SwiftUI swift · at 13:15 ↗
// Large content viewer support in SwiftUI

import SwiftUI

struct FigureBar: View {
    @Binding var selectedFigure: Figure
    
    var body: some View {
       HStack {
            ForEach(Figure.allCases) { figure in
                FigureButton(figure: figure, isSelected: selectedFigure == figure)
                    .onTapGesture {
                        selectedFigure = figure
                    }
                    .accessibilityShowsLargeContentViewer {
                        Label(figure.imageTitle, systemImage: figure.systemImage)
                    }
            }
        }
    }
}
Add large content viewer support with UIKit swift · at 13:45 ↗
// Large content viewer support in UIKit

import UIKit

class FigureCell: UIStackView {
    var systemImageName: String!
    var imageTitle: String!
    var imageLabel: UILabel!
    var titleImageView: UIImageView!
    
    required init(coder: NSCoder) {
        super.init(coder: coder)
        setupFigureCell()
    }
    
    init(systemImageName: String, imageTitle: String) {
        super.init(frame: .zero)
        
        self.systemImageName = systemImageName
        self.imageTitle = imageTitle
        
        setupFigureCell()

        self.addInteraction(UILargeContentViewerInteraction())
        self.showsLargeContentViewer = true
        self.largeContentImage = UIImage(systemName: systemImageName)
        self.scalesLargeContentImage = true
        self.largeContentTitle = imageTitle
    }
}

Resources