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 ↗Chapters
Code shown on screen · 11 snippets
Built-in text styles with SwiftUI
// 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
// 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
// Dynamic layout in SwiftUI
import SwiftUI
struct FigureCell: View {
(\.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
// Dynamic layout in SwiftUI
import SwiftUI
struct FigureContentView: View {
(\.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
// 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
// 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
// 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
// Scaling images in SwiftUI
import SwiftUI
struct ContentView: View {
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
// 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
// Large content viewer support in SwiftUI
import SwiftUI
struct FigureBar: View {
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
// 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
}
}