Delivering an exceptional accessibility experience
At a glance
| Item | Summary |
|---|---|
| Purpose | Demonstrate custom actions, adjustable elements, grouped statistics, modal accessibility, and change notifications. |
| Architecture | One UIKit view controller coordinates a small model and several specialized views. |
| State strategy | currentlyFocusedDog is the controller’s source of truth and propagates to the carousel, statistics, and shelter views. |
| Boundary | Single app target; no extension, persistence service, or custom protocol layer. |
Project structure
ExceptionalAccessibility/
├── ViewController.swift
├── Dog.swift
├── DogCarouselContainerView.swift
├── DogStatsView.swift
├── DogModalView.swift
└── DogCollectionViewCell.swift
Files are organized by screen component rather than formal architectural layers.
Overall architecture
flowchart LR
VC["ViewController"] --> Dogs["Dog values"]
VC --> Collection["UICollectionView"]
VC --> Carousel["DogCarouselContainerView"]
VC --> Stats["DogStatsView"]
VC --> Modal["DogModalView"]
Carousel --> AX["CarouselAccessibilityElement"]
Stats --> Grouped["Grouped UIAccessibilityElement values"]
Reference code
ExceptionalAccessibility/ViewController.swift:32 — one property broadcasts the selected model (abridged).
var currentlyFocusedDog: Dog? {
didSet {
dogStatsView.dog = currentlyFocusedDog
carouselContainerView.currentDog = currentlyFocusedDog
shelterNameLabel.text = currentlyFocusedDog?.shelterName
shelterInfoView.accessibilityLabel = currentlyFocusedDog?.shelterName
}
}Ownership and state
classDiagram
ViewController *-- Dog : dogs
ViewController o-- DogCarouselContainerView : outlet
ViewController o-- DogStatsView : outlet
DogCarouselContainerView *-- CarouselAccessibilityElement : cached
DogStatsView *-- UIAccessibilityElement : cached group
CarouselAccessibilityElement o-- DogCarouselContainerView : accessibilityContainer
Ownership evidence
ExceptionalAccessibility/DogCarouselContainerView.swift:189 — the container lazily owns and invalidates its accessibility projection (abridged).
private var _accessibilityElements: [Any]?
override var accessibilityElements: [Any]? {
if _accessibilityElements == nil {
let element = CarouselAccessibilityElement(
accessibilityContainer: self,
dog: currentDog
)
self.carouselAccessibilityElement = element
}
return _accessibilityElements
}The controller owns selection state. Each custom view owns only the accessibility objects derived from its current dog and clears its cache when that input changes.
Class and protocol design
The sample uses concrete UIKit subclasses: CarouselAccessibilityElement: UIAccessibilityElement, DogCarouselContainerView: UIView, and DogStatsView: UIView. ViewController directly adopts framework data-source/delegate protocols at ExceptionalAccessibility/ViewController.swift:15. There is no app-defined protocol because the components communicate through outlets and properties.
Access control
| Symbol | Access | Why it fits |
|---|---|---|
_accessibilityElements in carousel/stats |
private |
Cache consistency must remain inside each view (ExceptionalAccessibility/DogStatsView.swift:52). |
| Most outlets and model state | internal default | Storyboard wiring and the small sample share them across files. |
Dog fields |
internal | The value is target-local and directly rendered by several views. |
| Public API | none | The archive builds an application, not a library. |
The underscore marks a backing cache; private enforces its invalidation contract. Broader internal visibility is educational convenience, not a published surface.
Logic ownership and placement
| Logic | Owner | Placement reason |
|---|---|---|
| Current-dog propagation and collection scrolling | ViewController |
It coordinates all screen components. |
| Adjustable VoiceOver behavior | CarouselAccessibilityElement |
It is the semantic replacement for the carousel (ExceptionalAccessibility/DogCarouselContainerView.swift:133). |
| Grouping label pairs | DogStatsView |
It knows label frames and content. |
| Modal isolation | DogModalView |
The overlay declares itself modal at ExceptionalAccessibility/DogModalView.swift:51. |
Design patterns
| Pattern | Evidence | Purpose |
|---|---|---|
| Coordinator-by-controller | currentlyFocusedDog.didSet |
Synchronizes multiple views from one selection. |
| Accessibility adapter | Custom element turns a collection into an adjustable picker | Changes semantics without changing the visual UI. |
| Cached derived view | Accessibility element arrays persist until input changes | Gives VoiceOver stable object identity. |
| Framework delegate | Collection protocols on the controller | Keeps the small screen direct. |
Naming conventions
- Domain names (
Dog) stay separate from UI role suffixes (View,ViewController,Element). currentDogandcurrentlyFocusedDogcommunicate selection semantics.- Accessibility overrides retain UIKit’s framework vocabulary.
Architecture takeaways
- Keep one authoritative selection and push it to dependent components.
- Cache custom accessibility elements when assistive navigation requires stable identity.
- Place geometry-aware accessibility grouping inside the owning view.
- The design is intentionally concrete and code-light; extra service or protocol layers would obscure the examples.
Source map
| Source | Role |
|---|---|
ExceptionalAccessibility/Dog.swift:13 |
Domain value and fixtures |
ExceptionalAccessibility/ViewController.swift:45 |
Custom actions and screen coordination |
ExceptionalAccessibility/DogCarouselContainerView.swift:22 |
Adjustable custom element |
ExceptionalAccessibility/DogStatsView.swift:28 |
Input-driven cache invalidation |
ExceptionalAccessibility/DogModalView.swift:38 |
Overlay lifecycle |
ExceptionalAccessibility/AppDelegate.swift:9 |
App entry point |