2026 SwiftUI & UI FrameworksAccessibility & Inclusion
WWDC26 · 20 min · SwiftUI & UI Frameworks / Accessibility & Inclusion
Enhance the accessibility of your reading app
Learn how to create robust reading experiences for VoiceOver, Speak Screen, and more. Find out how to provide intuitive text selection, clear navigation between lines and paragraphs, and continuous reading across individual elements and multiple pages.
Watch at developer.apple.com ↗Chapters
Code shown on screen · 5 snippets
Link text elements together with navigation APIs
// Link text elements together with navigation APIs
import UIKit
class TravelGuidePageController: UIViewController {
var paragraphs: [TravelGuideParagraph]
func configureNavigationElements() {
for (index, paragraph) in paragraphs.enumerated() {
if index + 1 < paragraphs.count {
paragraph.accessibilityNextTextNavigationElement = paragraphs[index + 1]
}
if index - 1 >= 0 {
paragraph.accessibilityPreviousTextNavigationElement = paragraphs[index - 1]
}
}
}
} Link text elements together with a linked group
// Link text elements together with a linked group
import SwiftUI
struct PageView : View {
private var pageNamespace
var paragraphs: [String
var pageNumber: Int
var body: some View {
Text(paragraphs[0])
.textSelection(.enabled)
.accessibilityLinkedGroup(id: pageNumber, in: pageNamespace)
Text(paragraphs[1])
.textSelection(.enabled)
.accessibilityLinkedGroup(id: pageNumber, in: pageNamespace)
}
} Turn pages automatically after reading
// Turn pages automatically after reading
import UIKit
class TravelGuidePageController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.lastParagraphView.accessibilityTraits.insert(.causesPageTurn)
}
override func accessibilityScroll(_ direction: UIAccessibilityScrollDirection) -> Bool {
moveToPage(direction)
var scrollString = "Page \(currentPage) of \(pages.count)"
UIAccessibility.post(notification: .pageScrolled, argument: scrollString)
return true
}
} Add actions to the editor rotor
// Add actions to the editor rotor
import UIKit
class TravelGuideParagraph: UITextView {
override var accessibilityCustomActions: [UIAccessibilityCustomAction]? {
get {
let saveAction = UIAccessibilityCustomAction(name: "Save Recommendation") { _ in
self.saveRecommendation()
}
saveAction.category = UIAccessibilityCustomAction.editCategory
return (super.accessibilityCustomActions ?? []) + [saveAction]
}
set { }
}
private func saveRecommendation() -> Bool {
...
return true
}
} Adopt UITextInput
// Adopt UITextInput
import UIKit
class ScannedPage: UIView, UITextInput {
override init(frame: CGRect) {
super.init(frame: frame)
let interaction = UITextInteraction(for: .nonEditable)
interaction.textInput = self
addInteraction(interaction)
}
func selectionRects(for range: UITextRange) -> [UITextSelectionRect] {
var rects: [UITextSelectionRect] = []
let startLine = lineIndex(for: range.start)
let endLine = lineIndex(for: range.end)
for line in startLine...endLine {
let rect = selectionRectFromImage(for: range, in: line)
rects.append(rect)
}
return rects
}
func text(in range: UITextRange) -> String? {
let nsRange = nsRange(from: range)
guard let range = Range(nsRange, in: scannedText) else {
return nil
}
return String(scannedText[range])
}
var tokenizer: any UITextInputTokenizer { CustomHandwritingTokenizer(textInput: self) }
weak var inputDelegate: UITextInputDelegate?
var selectedTextRange: UITextRange? {
// Update visuals when assistive technologies change selection
willSet { inputDelegate?.selectionWillChange(self) }
didSet { inputDelegate?.selectionDidChange(self) }
}
} Resources
Related sessions
-
9 min