buildanything 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +55 -0
- package/README.md +71 -61
- package/agents/ios-app-review-guardian.md +49 -0
- package/agents/ios-foundation-models-specialist.md +46 -0
- package/agents/ios-storekit-specialist.md +52 -0
- package/agents/ios-swift-architect.md +102 -0
- package/agents/ios-swift-search.md +130 -0
- package/agents/ios-swift-ui-design.md +104 -0
- package/commands/build.md +80 -176
- package/commands/fix.md +65 -0
- package/commands/setup.md +73 -0
- package/commands/ux-review.md +63 -0
- package/commands/verify.md +72 -0
- package/hooks/session-start +18 -1
- package/package.json +5 -2
- package/protocols/brainstorm.md +99 -0
- package/protocols/build-fix.md +52 -0
- package/protocols/cleanup.md +54 -0
- package/protocols/design.md +269 -0
- package/protocols/eval-harness.md +61 -0
- package/protocols/fake-data-detector.md +64 -0
- package/protocols/ios-context.md +235 -0
- package/protocols/ios-frameworks-map.md +323 -0
- package/protocols/ios-phase-branches.md +162 -0
- package/protocols/ios-preflight.md +27 -0
- package/protocols/metric-loop.md +93 -0
- package/protocols/planning.md +87 -0
- package/protocols/smoke-test.md +110 -0
- package/protocols/verify.md +67 -0
- package/protocols/web-phase-branches.md +201 -0
- package/skills/ios/_VENDORED.md +60 -0
- package/skills/ios/activitykit/LICENSE +131 -0
- package/skills/ios/activitykit/SKILL.md +505 -0
- package/skills/ios/activitykit/references/activitykit-patterns.md +868 -0
- package/skills/ios/app-intents/LICENSE +131 -0
- package/skills/ios/app-intents/SKILL.md +494 -0
- package/skills/ios/app-intents/references/appintents-advanced.md +1076 -0
- package/skills/ios/apple-on-device-ai/LICENSE +131 -0
- package/skills/ios/apple-on-device-ai/SKILL.md +505 -0
- package/skills/ios/apple-on-device-ai/references/coreml-conversion.md +425 -0
- package/skills/ios/apple-on-device-ai/references/coreml-optimization.md +344 -0
- package/skills/ios/apple-on-device-ai/references/foundation-models.md +508 -0
- package/skills/ios/apple-on-device-ai/references/mlx-swift.md +285 -0
- package/skills/ios/ios-26-platform/SKILL.md +53 -0
- package/skills/ios/ios-26-platform/references/automatic-adoption.md +161 -0
- package/skills/ios/ios-26-platform/references/backward-compat.md +238 -0
- package/skills/ios/ios-26-platform/references/liquid-glass.md +255 -0
- package/skills/ios/ios-26-platform/references/swiftui-apis.md +277 -0
- package/skills/ios/ios-26-platform/references/toolbar-navigation.md +250 -0
- package/skills/ios/ios-bootstrap/SKILL.md +98 -0
- package/skills/ios/ios-bootstrap/references/apple-docs-mcp-config.md +28 -0
- package/skills/ios/ios-bootstrap/references/new-project-dialog.md +41 -0
- package/skills/ios/ios-bootstrap/references/xcode-mcp-config.md +29 -0
- package/skills/ios/ios-debugger-agent/LICENSE +21 -0
- package/skills/ios/ios-debugger-agent/SKILL.md +58 -0
- package/skills/ios/ios-debugger-agent/agents/openai.yaml +4 -0
- package/skills/ios/ios-entitlements-generator/SKILL.md +47 -0
- package/skills/ios/ios-hig/SKILL.md +41 -0
- package/skills/ios/ios-hig/references/accessibility.md +81 -0
- package/skills/ios/ios-hig/references/content.md +142 -0
- package/skills/ios/ios-hig/references/feedback.md +123 -0
- package/skills/ios/ios-hig/references/interaction.md +199 -0
- package/skills/ios/ios-hig/references/performance-platform.md +129 -0
- package/skills/ios/ios-hig/references/privacy-permissions.md +181 -0
- package/skills/ios/ios-hig/references/visual-design.md +84 -0
- package/skills/ios/ios-info-plist-hardening/SKILL.md +130 -0
- package/skills/ios/ios-maestro-flow-author/SKILL.md +68 -0
- package/skills/ios/ios-maestro-flow-author/references/input-and-scroll.yaml +17 -0
- package/skills/ios/ios-maestro-flow-author/references/modal-and-dismiss.yaml +14 -0
- package/skills/ios/ios-maestro-flow-author/references/onboarding-flow.yaml +16 -0
- package/skills/ios/ios-maestro-flow-author/references/tab-navigation.yaml +13 -0
- package/skills/ios/ios-maestro-flow-author/references/tap-and-assert.yaml +9 -0
- package/skills/ios/swift-accessibility/LICENSE +21 -0
- package/skills/ios/swift-accessibility/SKILL.md +371 -0
- package/skills/ios/swift-accessibility/examples/before-after-appkit.md +446 -0
- package/skills/ios/swift-accessibility/examples/before-after-swiftui.md +441 -0
- package/skills/ios/swift-accessibility/examples/before-after-uikit.md +464 -0
- package/skills/ios/swift-accessibility/references/assistive-access.md +441 -0
- package/skills/ios/swift-accessibility/references/display-settings.md +491 -0
- package/skills/ios/swift-accessibility/references/dynamic-type.md +420 -0
- package/skills/ios/swift-accessibility/references/media-accessibility.md +421 -0
- package/skills/ios/swift-accessibility/references/motor-input.md +393 -0
- package/skills/ios/swift-accessibility/references/nutrition-labels.md +362 -0
- package/skills/ios/swift-accessibility/references/platform-specifics.md +515 -0
- package/skills/ios/swift-accessibility/references/semantic-structure.md +585 -0
- package/skills/ios/swift-accessibility/references/testing-auditing.md +507 -0
- package/skills/ios/swift-accessibility/references/voice-control.md +317 -0
- package/skills/ios/swift-accessibility/references/voiceover-swiftui.md +584 -0
- package/skills/ios/swift-accessibility/references/voiceover-uikit.md +519 -0
- package/skills/ios/swift-accessibility/references/wcag-mapping.md +167 -0
- package/skills/ios/swift-accessibility/resources/audit-template.swift +128 -0
- package/skills/ios/swift-accessibility/resources/qa-checklist.md +258 -0
- package/skills/ios/swift-concurrency/LICENSE +21 -0
- package/skills/ios/swift-concurrency/SKILL.md +171 -0
- package/skills/ios/swift-concurrency/references/_index.md +50 -0
- package/skills/ios/swift-concurrency/references/actors.md +660 -0
- package/skills/ios/swift-concurrency/references/async-algorithms.md +847 -0
- package/skills/ios/swift-concurrency/references/async-await-basics.md +266 -0
- package/skills/ios/swift-concurrency/references/async-sequences.md +710 -0
- package/skills/ios/swift-concurrency/references/core-data.md +560 -0
- package/skills/ios/swift-concurrency/references/glossary.md +135 -0
- package/skills/ios/swift-concurrency/references/linting.md +155 -0
- package/skills/ios/swift-concurrency/references/memory-management.md +569 -0
- package/skills/ios/swift-concurrency/references/migration.md +1104 -0
- package/skills/ios/swift-concurrency/references/performance.md +593 -0
- package/skills/ios/swift-concurrency/references/sendable.md +598 -0
- package/skills/ios/swift-concurrency/references/tasks.md +636 -0
- package/skills/ios/swift-concurrency/references/testing.md +592 -0
- package/skills/ios/swift-concurrency/references/threading.md +495 -0
- package/skills/ios/swift-security-expert/LICENSE +21 -0
- package/skills/ios/swift-security-expert/SKILL.md +470 -0
- package/skills/ios/swift-security-expert/references/biometric-authentication.md +565 -0
- package/skills/ios/swift-security-expert/references/certificate-trust.md +592 -0
- package/skills/ios/swift-security-expert/references/common-anti-patterns.md +690 -0
- package/skills/ios/swift-security-expert/references/compliance-owasp-mapping.md +537 -0
- package/skills/ios/swift-security-expert/references/credential-storage-patterns.md +721 -0
- package/skills/ios/swift-security-expert/references/cryptokit-public-key.md +505 -0
- package/skills/ios/swift-security-expert/references/cryptokit-symmetric.md +497 -0
- package/skills/ios/swift-security-expert/references/keychain-access-control.md +508 -0
- package/skills/ios/swift-security-expert/references/keychain-fundamentals.md +596 -0
- package/skills/ios/swift-security-expert/references/keychain-item-classes.md +476 -0
- package/skills/ios/swift-security-expert/references/keychain-sharing.md +458 -0
- package/skills/ios/swift-security-expert/references/migration-legacy-stores.md +727 -0
- package/skills/ios/swift-security-expert/references/secure-enclave.md +539 -0
- package/skills/ios/swift-security-expert/references/testing-security-code.md +781 -0
- package/skills/ios/swift-testing-expert/LICENSE +21 -0
- package/skills/ios/swift-testing-expert/SKILL.md +79 -0
- package/skills/ios/swift-testing-expert/references/_index.md +12 -0
- package/skills/ios/swift-testing-expert/references/async-testing-and-waiting.md +127 -0
- package/skills/ios/swift-testing-expert/references/expectations.md +145 -0
- package/skills/ios/swift-testing-expert/references/fundamentals.md +141 -0
- package/skills/ios/swift-testing-expert/references/migration-from-xctest.md +127 -0
- package/skills/ios/swift-testing-expert/references/parallelization-and-isolation.md +95 -0
- package/skills/ios/swift-testing-expert/references/parameterized-testing.md +284 -0
- package/skills/ios/swift-testing-expert/references/performance-and-best-practices.md +187 -0
- package/skills/ios/swift-testing-expert/references/traits-and-tags.md +114 -0
- package/skills/ios/swift-testing-expert/references/xcode-workflows.md +70 -0
- package/skills/ios/swiftdata-pro/LICENSE +21 -0
- package/skills/ios/swiftdata-pro/SKILL.md +102 -0
- package/skills/ios/swiftdata-pro/agents/openai.yaml +10 -0
- package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.png +0 -0
- package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.svg +29 -0
- package/skills/ios/swiftdata-pro/references/class-inheritance.md +104 -0
- package/skills/ios/swiftdata-pro/references/cloudkit.md +10 -0
- package/skills/ios/swiftdata-pro/references/core-rules.md +20 -0
- package/skills/ios/swiftdata-pro/references/indexing.md +27 -0
- package/skills/ios/swiftdata-pro/references/predicates.md +73 -0
- package/skills/ios/swiftui-design-principles/AGENTS.md +21 -0
- package/skills/ios/swiftui-design-principles/LICENSE +21 -0
- package/skills/ios/swiftui-design-principles/README.md +41 -0
- package/skills/ios/swiftui-design-principles/SKILL.md +605 -0
- package/skills/ios/swiftui-design-principles/metadata.json +10 -0
- package/skills/ios/swiftui-liquid-glass/LICENSE +21 -0
- package/skills/ios/swiftui-liquid-glass/SKILL.md +95 -0
- package/skills/ios/swiftui-liquid-glass/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-liquid-glass/references/liquid-glass.md +280 -0
- package/skills/ios/swiftui-performance-audit/LICENSE +21 -0
- package/skills/ios/swiftui-performance-audit/SKILL.md +111 -0
- package/skills/ios/swiftui-performance-audit/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-performance-audit/references/code-smells.md +150 -0
- package/skills/ios/swiftui-performance-audit/references/demystify-swiftui-performance-wwdc23.md +46 -0
- package/skills/ios/swiftui-performance-audit/references/optimizing-swiftui-performance-instruments.md +29 -0
- package/skills/ios/swiftui-performance-audit/references/profiling-intake.md +44 -0
- package/skills/ios/swiftui-performance-audit/references/report-template.md +47 -0
- package/skills/ios/swiftui-performance-audit/references/understanding-hangs-in-your-app.md +33 -0
- package/skills/ios/swiftui-performance-audit/references/understanding-improving-swiftui-performance.md +52 -0
- package/skills/ios/swiftui-pro/LICENSE +21 -0
- package/skills/ios/swiftui-pro/SKILL.md +108 -0
- package/skills/ios/swiftui-pro/agents/openai.yaml +10 -0
- package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.png +0 -0
- package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.svg +29 -0
- package/skills/ios/swiftui-pro/references/accessibility.md +13 -0
- package/skills/ios/swiftui-pro/references/api.md +39 -0
- package/skills/ios/swiftui-pro/references/data.md +43 -0
- package/skills/ios/swiftui-pro/references/design.md +31 -0
- package/skills/ios/swiftui-pro/references/hygiene.md +9 -0
- package/skills/ios/swiftui-pro/references/navigation.md +14 -0
- package/skills/ios/swiftui-pro/references/performance.md +46 -0
- package/skills/ios/swiftui-pro/references/swift.md +56 -0
- package/skills/ios/swiftui-pro/references/views.md +35 -0
- package/skills/ios/swiftui-ui-patterns/LICENSE +21 -0
- package/skills/ios/swiftui-ui-patterns/SKILL.md +100 -0
- package/skills/ios/swiftui-ui-patterns/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-ui-patterns/references/app-wiring.md +201 -0
- package/skills/ios/swiftui-ui-patterns/references/async-state.md +96 -0
- package/skills/ios/swiftui-ui-patterns/references/components-index.md +50 -0
- package/skills/ios/swiftui-ui-patterns/references/controls.md +57 -0
- package/skills/ios/swiftui-ui-patterns/references/deeplinks.md +66 -0
- package/skills/ios/swiftui-ui-patterns/references/focus.md +90 -0
- package/skills/ios/swiftui-ui-patterns/references/form.md +97 -0
- package/skills/ios/swiftui-ui-patterns/references/grids.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/haptics.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/input-toolbar.md +51 -0
- package/skills/ios/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
- package/skills/ios/swiftui-ui-patterns/references/list.md +86 -0
- package/skills/ios/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
- package/skills/ios/swiftui-ui-patterns/references/macos-settings.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/matched-transitions.md +59 -0
- package/skills/ios/swiftui-ui-patterns/references/media.md +73 -0
- package/skills/ios/swiftui-ui-patterns/references/menu-bar.md +101 -0
- package/skills/ios/swiftui-ui-patterns/references/navigationstack.md +159 -0
- package/skills/ios/swiftui-ui-patterns/references/overlay.md +45 -0
- package/skills/ios/swiftui-ui-patterns/references/performance.md +62 -0
- package/skills/ios/swiftui-ui-patterns/references/previews.md +48 -0
- package/skills/ios/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
- package/skills/ios/swiftui-ui-patterns/references/scrollview.md +87 -0
- package/skills/ios/swiftui-ui-patterns/references/searchable.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/sheets.md +155 -0
- package/skills/ios/swiftui-ui-patterns/references/split-views.md +72 -0
- package/skills/ios/swiftui-ui-patterns/references/tabview.md +114 -0
- package/skills/ios/swiftui-ui-patterns/references/theming.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/title-menus.md +93 -0
- package/skills/ios/swiftui-ui-patterns/references/top-bar.md +49 -0
- package/skills/ios/swiftui-view-refactor/LICENSE +21 -0
- package/skills/ios/swiftui-view-refactor/SKILL.md +207 -0
- package/skills/ios/swiftui-view-refactor/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-view-refactor/references/mv-patterns.md +161 -0
- package/skills/ios/widgetkit/LICENSE +131 -0
- package/skills/ios/widgetkit/SKILL.md +502 -0
- package/skills/ios/widgetkit/references/widgetkit-advanced.md +871 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
# Dynamic Type and Larger Text
|
|
2
|
+
|
|
3
|
+
Dynamic Type lets users choose their preferred text size system-wide. Apps must support it to qualify for the **Larger Text** Accessibility Nutrition Label.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
- [Text Style Reference](#text-style-reference)
|
|
7
|
+
- [SwiftUI Implementation](#swiftui-implementation)
|
|
8
|
+
- [@ScaledMetric](#scaledmetric)
|
|
9
|
+
- [Large Content Viewer](#large-content-viewer)
|
|
10
|
+
- [Adaptive Layout Patterns](#adaptive-layout-patterns)
|
|
11
|
+
- [UIKit Implementation](#uikit-implementation)
|
|
12
|
+
- [Testing](#testing)
|
|
13
|
+
- [Common Failures](#common-failures)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Text Style Reference
|
|
18
|
+
|
|
19
|
+
Always use text styles — never hardcode font sizes. Text styles scale automatically with the user's preferred size.
|
|
20
|
+
|
|
21
|
+
| Style | Default Size | Use Case |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| `.largeTitle` | 34pt | Main screen titles |
|
|
24
|
+
| `.title` | 28pt | Section titles |
|
|
25
|
+
| `.title2` | 22pt | Subsection titles |
|
|
26
|
+
| `.title3` | 20pt | Card or group titles |
|
|
27
|
+
| `.headline` | 17pt (semibold) | Table row headers, important labels |
|
|
28
|
+
| `.subheadline` | 15pt | Supporting text next to headline |
|
|
29
|
+
| `.body` | 17pt | Primary reading text |
|
|
30
|
+
| `.callout` | 16pt | Annotations, asides |
|
|
31
|
+
| `.footnote` | 13pt | Fine print, timestamps |
|
|
32
|
+
| `.caption` | 12pt | Image captions, secondary labels |
|
|
33
|
+
| `.caption2` | 11pt | Smallest readable text |
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## SwiftUI Implementation
|
|
38
|
+
|
|
39
|
+
### Standard Text Styles — Automatic Scaling
|
|
40
|
+
|
|
41
|
+
No extra work needed. SwiftUI scales these automatically.
|
|
42
|
+
|
|
43
|
+
```swift
|
|
44
|
+
// ✅ Scales with Dynamic Type
|
|
45
|
+
Text("Welcome back")
|
|
46
|
+
.font(.title)
|
|
47
|
+
|
|
48
|
+
Text("Your recent orders")
|
|
49
|
+
.font(.headline)
|
|
50
|
+
|
|
51
|
+
// ❌ Hardcoded — does NOT scale
|
|
52
|
+
Text("Welcome back")
|
|
53
|
+
.font(.system(size: 28))
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Custom Fonts with Text Styles
|
|
57
|
+
|
|
58
|
+
```swift
|
|
59
|
+
// ✅ Custom font that scales with the body text style
|
|
60
|
+
Text("Note")
|
|
61
|
+
.font(.custom("Merriweather-Regular", size: 17, relativeTo: .body))
|
|
62
|
+
|
|
63
|
+
// ❌ Custom font with fixed size
|
|
64
|
+
Text("Note")
|
|
65
|
+
.font(.custom("Merriweather-Regular", size: 17))
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Reading Dynamic Type Size
|
|
69
|
+
|
|
70
|
+
```swift
|
|
71
|
+
@Environment(\.dynamicTypeSize) var dynamicTypeSize
|
|
72
|
+
|
|
73
|
+
var body: some View {
|
|
74
|
+
if dynamicTypeSize >= .accessibility1 {
|
|
75
|
+
// Large type layout
|
|
76
|
+
VStack(alignment: .leading) {
|
|
77
|
+
avatar
|
|
78
|
+
nameAndDetails
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
// Standard layout
|
|
82
|
+
HStack {
|
|
83
|
+
avatar
|
|
84
|
+
nameAndDetails
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### `DynamicTypeSize` Reference
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
.xSmall .small .medium .large (default) .xLarge .xxLarge .xxxLarge
|
|
94
|
+
.accessibility1 .accessibility2 .accessibility3 .accessibility4 .accessibility5
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`.accessibility5` is the maximum size. Test your layout at this size.
|
|
98
|
+
|
|
99
|
+
### Limiting Dynamic Type Size
|
|
100
|
+
|
|
101
|
+
Only limit size when the layout genuinely cannot accommodate larger text. Always provide alternatives.
|
|
102
|
+
|
|
103
|
+
```swift
|
|
104
|
+
// Limit a compact thumbnail — Large Content Viewer compensates
|
|
105
|
+
ThumbnailView()
|
|
106
|
+
.dynamicTypeSize(.xSmall ... .accessibility2)
|
|
107
|
+
.accessibilityShowsLargeContentViewer() // required when limiting!
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## @ScaledMetric
|
|
113
|
+
|
|
114
|
+
`@ScaledMetric` scales ANY numeric value (spacing, icon size, corner radius) proportionally with the user's text size preference.
|
|
115
|
+
|
|
116
|
+
```swift
|
|
117
|
+
struct ProfileRow: View {
|
|
118
|
+
@ScaledMetric(relativeTo: .body) private var avatarSize: CGFloat = 44
|
|
119
|
+
@ScaledMetric(relativeTo: .body) private var spacing: CGFloat = 12
|
|
120
|
+
@ScaledMetric(relativeTo: .caption) private var badgeSize: CGFloat = 16
|
|
121
|
+
|
|
122
|
+
var body: some View {
|
|
123
|
+
HStack(spacing: spacing) {
|
|
124
|
+
Avatar()
|
|
125
|
+
.frame(width: avatarSize, height: avatarSize)
|
|
126
|
+
VStack(alignment: .leading, spacing: spacing / 3) {
|
|
127
|
+
nameLabel
|
|
128
|
+
timestampLabel
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**`relativeTo:`** — specifies which text style the metric scales with. Use the same style as the adjacent text.
|
|
136
|
+
|
|
137
|
+
```swift
|
|
138
|
+
// Scales with body text
|
|
139
|
+
@ScaledMetric(relativeTo: .body) var iconSize: CGFloat = 24
|
|
140
|
+
|
|
141
|
+
// Scales with headline text
|
|
142
|
+
@ScaledMetric(relativeTo: .headline) var rowHeight: CGFloat = 44
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Large Content Viewer
|
|
148
|
+
|
|
149
|
+
The Large Content Viewer shows an enlarged version of UI elements that cannot scale with Dynamic Type — typically items in fixed-size containers like tab bars and toolbars. Users long-press to see the enlarged version.
|
|
150
|
+
|
|
151
|
+
### When to Use
|
|
152
|
+
|
|
153
|
+
Use when an element's size is intentionally constrained (for layout reasons) and cannot grow with Dynamic Type.
|
|
154
|
+
|
|
155
|
+
- Tab bar items
|
|
156
|
+
- Toolbar buttons
|
|
157
|
+
- Badge labels
|
|
158
|
+
- Navigation bar title when using custom small sizes
|
|
159
|
+
|
|
160
|
+
**Do NOT use as a replacement** for supporting Dynamic Type in regular content.
|
|
161
|
+
|
|
162
|
+
### SwiftUI
|
|
163
|
+
|
|
164
|
+
```swift
|
|
165
|
+
// Tab bar item — automatically handled by TabView
|
|
166
|
+
// For custom fixed-size elements:
|
|
167
|
+
|
|
168
|
+
Image(systemName: "bell.fill")
|
|
169
|
+
.font(.system(size: 20))
|
|
170
|
+
.frame(width: 44, height: 44)
|
|
171
|
+
.dynamicTypeSize(.xSmall ... .accessibility2) // size-constrained
|
|
172
|
+
.accessibilityShowsLargeContentViewer() // required!
|
|
173
|
+
.accessibilityLabel("Notifications")
|
|
174
|
+
|
|
175
|
+
// Custom content in the viewer
|
|
176
|
+
Image(systemName: "bell.fill")
|
|
177
|
+
.accessibilityShowsLargeContentViewer {
|
|
178
|
+
Label("Notifications", systemImage: "bell.fill")
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### UIKit — `UILargeContentViewerInteraction`
|
|
183
|
+
|
|
184
|
+
```swift
|
|
185
|
+
class CustomTabBarItem: UIView {
|
|
186
|
+
override init(frame: CGRect) {
|
|
187
|
+
super.init(frame: frame)
|
|
188
|
+
setupLargeContentViewer()
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private func setupLargeContentViewer() {
|
|
192
|
+
showsLargeContentViewer = true
|
|
193
|
+
largeContentTitle = "Library"
|
|
194
|
+
largeContentImage = UIImage(systemName: "books.vertical")
|
|
195
|
+
|
|
196
|
+
let interaction = UILargeContentViewerInteraction()
|
|
197
|
+
addInteraction(interaction)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Large Content Viewer Design Requirements
|
|
203
|
+
|
|
204
|
+
- Minimum element height: 28pt (system recommendation: 44pt)
|
|
205
|
+
- Short title (1–2 words max)
|
|
206
|
+
- Clear icon (SF Symbol or simple custom image)
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Adaptive Layout Patterns
|
|
211
|
+
|
|
212
|
+
At large text sizes, horizontal space becomes scarce. Common adaptations:
|
|
213
|
+
|
|
214
|
+
### HStack → VStack Switch
|
|
215
|
+
|
|
216
|
+
```swift
|
|
217
|
+
@Environment(\.dynamicTypeSize) var typeSize
|
|
218
|
+
|
|
219
|
+
var body: some View {
|
|
220
|
+
Group {
|
|
221
|
+
if typeSize >= .accessibility1 {
|
|
222
|
+
VStack(alignment: .leading, spacing: 8) { content }
|
|
223
|
+
} else {
|
|
224
|
+
HStack(spacing: 12) { content }
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Or using ViewThatFits (iOS 16+) — automatically picks the layout that fits
|
|
230
|
+
ViewThatFits {
|
|
231
|
+
HStack { content } // tried first
|
|
232
|
+
VStack { content } // fallback if HStack doesn't fit
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Truncation Strategy
|
|
237
|
+
|
|
238
|
+
```swift
|
|
239
|
+
// ✅ Wrap text, don't truncate primary content
|
|
240
|
+
Text(longTitle)
|
|
241
|
+
.fixedSize(horizontal: false, vertical: true) // allows vertical expansion
|
|
242
|
+
.lineLimit(nil)
|
|
243
|
+
|
|
244
|
+
// ✅ Truncate secondary content, keep primary readable
|
|
245
|
+
HStack {
|
|
246
|
+
Text(primaryLabel)
|
|
247
|
+
.lineLimit(2)
|
|
248
|
+
Text(secondaryLabel)
|
|
249
|
+
.lineLimit(1)
|
|
250
|
+
.foregroundStyle(.secondary)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// ❌ Truncates the only label — VoiceOver still reads it, but Visual isn't accessible
|
|
254
|
+
Text(importantLabel)
|
|
255
|
+
.lineLimit(1)
|
|
256
|
+
.truncationMode(.tail)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Avoiding Fixed Heights
|
|
260
|
+
|
|
261
|
+
```swift
|
|
262
|
+
// ❌ Clips text at large sizes
|
|
263
|
+
.frame(height: 44)
|
|
264
|
+
|
|
265
|
+
// ✅ Minimum height with uncapped growth
|
|
266
|
+
.frame(minHeight: 44)
|
|
267
|
+
|
|
268
|
+
// ✅ Or just let SwiftUI size it naturally
|
|
269
|
+
// (HStack/VStack children grow to fit their content)
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## UIKit Implementation
|
|
275
|
+
|
|
276
|
+
### Text Styles — `UIFont.preferredFont`
|
|
277
|
+
|
|
278
|
+
```swift
|
|
279
|
+
// ✅ Scales with Dynamic Type
|
|
280
|
+
label.font = UIFont.preferredFont(forTextStyle: .body)
|
|
281
|
+
label.adjustsFontForContentSizeCategory = true // required for updates
|
|
282
|
+
|
|
283
|
+
// ❌ Fixed size — doesn't scale
|
|
284
|
+
label.font = UIFont.systemFont(ofSize: 17)
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
`adjustsFontForContentSizeCategory = true` is essential — without it, the font is set once and never updates when the user changes their text size.
|
|
288
|
+
|
|
289
|
+
### Custom Fonts with `UIFontMetrics`
|
|
290
|
+
|
|
291
|
+
```swift
|
|
292
|
+
let customFont = UIFont(name: "Merriweather-Regular", size: 17)!
|
|
293
|
+
label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: customFont)
|
|
294
|
+
label.adjustsFontForContentSizeCategory = true
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Scaling Non-Font Values
|
|
298
|
+
|
|
299
|
+
```swift
|
|
300
|
+
// Scale spacing, icon sizes, etc.
|
|
301
|
+
let baseSpacing: CGFloat = 8
|
|
302
|
+
let scaledSpacing = UIFontMetrics.default.scaledValue(for: baseSpacing)
|
|
303
|
+
|
|
304
|
+
// Scale relative to a specific text style
|
|
305
|
+
let bodyMetrics = UIFontMetrics(forTextStyle: .body)
|
|
306
|
+
let iconSize = bodyMetrics.scaledValue(for: 24)
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Reacting to Size Changes
|
|
310
|
+
|
|
311
|
+
```swift
|
|
312
|
+
// Pre-iOS 17:
|
|
313
|
+
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { // Deprecated in iOS 17
|
|
314
|
+
super.traitCollectionDidChange(previousTraitCollection)
|
|
315
|
+
|
|
316
|
+
if traitCollection.preferredContentSizeCategory != previousTraitCollection?.preferredContentSizeCategory {
|
|
317
|
+
updateLayout()
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// iOS 17+ replacement:
|
|
322
|
+
registerForTraitChanges([UITraitPreferredContentSizeCategory.self]) { (self: Self, _) in
|
|
323
|
+
self.updateLayout()
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Or observe the notification (works on all versions)
|
|
327
|
+
NotificationCenter.default.addObserver(
|
|
328
|
+
self,
|
|
329
|
+
selector: #selector(contentSizeCategoryDidChange),
|
|
330
|
+
name: UIContentSizeCategory.didChangeNotification,
|
|
331
|
+
object: nil
|
|
332
|
+
)
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### `UIContentSizeCategory` Reference
|
|
336
|
+
|
|
337
|
+
```swift
|
|
338
|
+
// Progression from smallest to largest:
|
|
339
|
+
let categoriesInOrder: [UIContentSizeCategory] = [
|
|
340
|
+
.extraSmall,
|
|
341
|
+
.small,
|
|
342
|
+
.medium,
|
|
343
|
+
.large, // default
|
|
344
|
+
.extraLarge,
|
|
345
|
+
.extraExtraLarge,
|
|
346
|
+
.extraExtraExtraLarge,
|
|
347
|
+
.accessibilityMedium,
|
|
348
|
+
.accessibilityLarge,
|
|
349
|
+
.accessibilityExtraLarge,
|
|
350
|
+
.accessibilityExtraExtraLarge,
|
|
351
|
+
.accessibilityExtraExtraExtraLarge
|
|
352
|
+
]
|
|
353
|
+
|
|
354
|
+
// Check if accessibility size is active
|
|
355
|
+
let isAccessibilitySize = traitCollection.preferredContentSizeCategory.isAccessibilityCategory
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### SF Symbols Scaling with Text
|
|
359
|
+
|
|
360
|
+
```swift
|
|
361
|
+
// Scale symbol with adjacent body text
|
|
362
|
+
let config = UIImage.SymbolConfiguration(textStyle: .body)
|
|
363
|
+
imageView.image = UIImage(systemName: "star.fill", withConfiguration: config)
|
|
364
|
+
|
|
365
|
+
// Scale to specific point size
|
|
366
|
+
let sizedConfig = UIImage.SymbolConfiguration(pointSize: 24, weight: .medium)
|
|
367
|
+
imageView.image = UIImage(systemName: "heart.fill", withConfiguration: sizedConfig)?
|
|
368
|
+
.applyingSymbolConfiguration(.preferringMulticolor()) // append configurations
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## Testing
|
|
374
|
+
|
|
375
|
+
### Minimum Testing Requirements
|
|
376
|
+
|
|
377
|
+
| Size | Category | Setting |
|
|
378
|
+
|---|---|---|
|
|
379
|
+
| Small | `.small` | Settings → Display & Text Size |
|
|
380
|
+
| Default | `.large` | Default |
|
|
381
|
+
| Large | `.extraExtraLarge` | ~150% scaling |
|
|
382
|
+
| Maximum | `.accessibilityExtraExtraExtraLarge` | 200%+ |
|
|
383
|
+
| watchOS max | — | 140%+ |
|
|
384
|
+
|
|
385
|
+
### SwiftUI Preview Testing
|
|
386
|
+
|
|
387
|
+
```swift
|
|
388
|
+
// Test at specific sizes
|
|
389
|
+
#Preview("Large Accessibility Size") {
|
|
390
|
+
ContentView()
|
|
391
|
+
.environment(\.dynamicTypeSize, .accessibility5)
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
#Preview("Default Size") {
|
|
395
|
+
ContentView()
|
|
396
|
+
.environment(\.dynamicTypeSize, .large)
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### Xcode Simulator
|
|
401
|
+
|
|
402
|
+
In Simulator: Hardware → Device Settings → Increase Contrast + Dynamic Text → drag to max.
|
|
403
|
+
|
|
404
|
+
Or in Accessibility Inspector (macOS): connect to Simulator and adjust font size.
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## Common Failures
|
|
409
|
+
|
|
410
|
+
| Failure | Fix |
|
|
411
|
+
|---|---|
|
|
412
|
+
| `.font(.system(size: 17))` | Use `.font(.body)` |
|
|
413
|
+
| Fixed frame clips text | Use `.frame(minHeight: 44)` not `height: 44` |
|
|
414
|
+
| HStack overflows at large sizes | Switch to `VStack` or `ViewThatFits` |
|
|
415
|
+
| Custom font doesn't scale | Add `relativeTo:` to `.custom()` or use `UIFontMetrics` |
|
|
416
|
+
| `adjustsFontForContentSizeCategory` missing | Set to `true` on all labels |
|
|
417
|
+
| Icon size stays fixed | Use `@ScaledMetric` or `UIFontMetrics.scaledValue` |
|
|
418
|
+
| Large Content Viewer not shown for tab items | Add `.accessibilityShowsLargeContentViewer()` explicitly |
|
|
419
|
+
| Single-line truncation loses information | Use `.lineLimit(nil)` or provide detail view on tap |
|
|
420
|
+
| Language testing missing | Test German (long words), Arabic (RTL), Japanese (tall characters) |
|