agy-superpowers 5.0.7 → 5.0.9
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/package.json +1 -1
- package/template/agent/rules/superpowers.md +54 -0
- package/template/agent/skills/api-design/SKILL.md +193 -0
- package/template/agent/skills/app-store-optimizer/SKILL.md +127 -0
- package/template/agent/skills/auth-and-identity/SKILL.md +167 -0
- package/template/agent/skills/backend-developer/SKILL.md +148 -0
- package/template/agent/skills/community-manager/SKILL.md +115 -0
- package/template/agent/skills/content-marketer/SKILL.md +111 -0
- package/template/agent/skills/conversion-optimizer/SKILL.md +142 -0
- package/template/agent/skills/copywriter/SKILL.md +114 -0
- package/template/agent/skills/cto-architect/SKILL.md +133 -0
- package/template/agent/skills/customer-success-manager/SKILL.md +126 -0
- package/template/agent/skills/data-analyst/SKILL.md +147 -0
- package/template/agent/skills/devops-engineer/SKILL.md +117 -0
- package/template/agent/skills/email-infrastructure/SKILL.md +164 -0
- package/template/agent/skills/frontend-developer/SKILL.md +172 -0
- package/template/agent/skills/frontend-developer/references/react-nextjs.md +343 -0
- package/template/agent/skills/frontend-developer/references/react-rules/_sections.md +46 -0
- package/template/agent/skills/frontend-developer/references/react-rules/_template.md +28 -0
- package/template/agent/skills/frontend-developer/references/react-rules/advanced-event-handler-refs.md +55 -0
- package/template/agent/skills/frontend-developer/references/react-rules/advanced-init-once.md +42 -0
- package/template/agent/skills/frontend-developer/references/react-rules/advanced-use-latest.md +39 -0
- package/template/agent/skills/frontend-developer/references/react-rules/async-api-routes.md +38 -0
- package/template/agent/skills/frontend-developer/references/react-rules/async-defer-await.md +80 -0
- package/template/agent/skills/frontend-developer/references/react-rules/async-dependencies.md +51 -0
- package/template/agent/skills/frontend-developer/references/react-rules/async-parallel.md +28 -0
- package/template/agent/skills/frontend-developer/references/react-rules/async-suspense-boundaries.md +99 -0
- package/template/agent/skills/frontend-developer/references/react-rules/bundle-barrel-imports.md +59 -0
- package/template/agent/skills/frontend-developer/references/react-rules/bundle-conditional.md +31 -0
- package/template/agent/skills/frontend-developer/references/react-rules/bundle-defer-third-party.md +49 -0
- package/template/agent/skills/frontend-developer/references/react-rules/bundle-dynamic-imports.md +35 -0
- package/template/agent/skills/frontend-developer/references/react-rules/bundle-preload.md +50 -0
- package/template/agent/skills/frontend-developer/references/react-rules/client-event-listeners.md +74 -0
- package/template/agent/skills/frontend-developer/references/react-rules/client-localstorage-schema.md +71 -0
- package/template/agent/skills/frontend-developer/references/react-rules/client-passive-event-listeners.md +48 -0
- package/template/agent/skills/frontend-developer/references/react-rules/client-swr-dedup.md +56 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-batch-dom-css.md +107 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-cache-function-results.md +80 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-cache-property-access.md +28 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-cache-storage.md +70 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-combine-iterations.md +32 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-early-exit.md +50 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-flatmap-filter.md +60 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-hoist-regexp.md +45 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-index-maps.md +37 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-length-check-first.md +49 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-min-max-loop.md +82 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-set-map-lookups.md +24 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-tosorted-immutable.md +57 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-activity.md +26 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-animate-svg-wrapper.md +47 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-conditional-render.md +40 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-content-visibility.md +38 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-hoist-jsx.md +46 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-hydration-no-flicker.md +82 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-hydration-suppress-warning.md +30 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-resource-hints.md +85 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-script-defer-async.md +68 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-svg-precision.md +28 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-usetransition-loading.md +75 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-defer-reads.md +39 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-dependencies.md +45 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-derived-state-no-effect.md +40 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-derived-state.md +29 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-functional-setstate.md +74 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-lazy-state-init.md +58 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-memo-with-default-value.md +38 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-memo.md +44 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-move-effect-to-event.md +45 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-no-inline-components.md +82 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-simple-expression-in-memo.md +35 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-split-combined-hooks.md +64 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-transitions.md +40 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-use-deferred-value.md +59 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-use-ref-transient-values.md +73 -0
- package/template/agent/skills/frontend-developer/references/react-rules/server-after-nonblocking.md +73 -0
- package/template/agent/skills/frontend-developer/references/react-rules/server-auth-actions.md +96 -0
- package/template/agent/skills/frontend-developer/references/react-rules/server-cache-lru.md +41 -0
- package/template/agent/skills/frontend-developer/references/react-rules/server-cache-react.md +76 -0
- package/template/agent/skills/frontend-developer/references/react-rules/server-dedup-props.md +65 -0
- package/template/agent/skills/frontend-developer/references/react-rules/server-hoist-static-io.md +142 -0
- package/template/agent/skills/frontend-developer/references/react-rules/server-parallel-fetching.md +83 -0
- package/template/agent/skills/frontend-developer/references/react-rules/server-serialization.md +38 -0
- package/template/agent/skills/frontend-developer/references/svelte-sveltekit.md +220 -0
- package/template/agent/skills/frontend-developer/references/vanilla.md +275 -0
- package/template/agent/skills/frontend-developer/references/vue-nuxt.md +289 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/_index.md +154 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/animation-class-based-technique.md +254 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/animation-state-driven-technique.md +291 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-async.md +97 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-data-flow.md +307 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-fallthrough-attrs.md +174 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-keep-alive.md +137 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-slots.md +216 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-suspense.md +228 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-teleport.md +108 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-transition-group.md +128 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-transition.md +125 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/composables.md +290 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/directives.md +162 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/perf-avoid-component-abstraction-in-lists.md +159 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/perf-v-once-v-memo-directives.md +182 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/perf-virtualize-large-lists.md +187 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/plugins.md +166 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/reactivity.md +344 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/render-functions.md +201 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/sfc.md +310 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/state-management.md +135 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/updated-hook-performance.md +187 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/_index.md +23 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/router-beforeenter-no-param-trigger.md +167 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/router-beforerouteenter-no-this.md +176 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/router-guard-async-await-pattern.md +227 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/router-navigation-guard-infinite-loop.md +187 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/router-navigation-guard-next-deprecated.md +150 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/router-param-change-no-lifecycle.md +181 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/router-simple-routing-cleanup.md +209 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/router-use-vue-router-for-production.md +183 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/_index.md +29 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/async-component-testing.md +163 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/teleport-testing-complexity.md +158 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-async-await-flushpromises.md +175 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-browser-vs-node-runners.md +208 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-component-blackbox-approach.md +144 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-composables-helper-wrapper.md +238 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-e2e-playwright-recommended.md +242 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-no-snapshot-only.md +197 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-pinia-store-setup.md +228 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-suspense-async-components.md +229 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-vitest-recommended-for-vue.md +204 -0
- package/template/agent/skills/game-design/SKILL.md +194 -0
- package/template/agent/skills/game-developer/SKILL.md +175 -0
- package/template/agent/skills/growth-hacker/SKILL.md +122 -0
- package/template/agent/skills/i18n-localization/SKILL.md +126 -0
- package/template/agent/skills/influencer-marketer/SKILL.md +141 -0
- package/template/agent/skills/mobile-developer/SKILL.md +194 -0
- package/template/agent/skills/mobile-developer/references/android-native.md +396 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-accessibility.md +36 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-architecture.md +52 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-coroutines.md +139 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-data-layer.md +51 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-emulator-skill.md +108 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-gradle-logic.md +126 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-retrofit.md +142 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-testing.md +102 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-viewmodel.md +43 -0
- package/template/agent/skills/mobile-developer/references/android-rules/coil-compose.md +74 -0
- package/template/agent/skills/mobile-developer/references/android-rules/compose-navigation.md +422 -0
- package/template/agent/skills/mobile-developer/references/android-rules/compose-performance-audit.md +199 -0
- package/template/agent/skills/mobile-developer/references/android-rules/compose-ui.md +49 -0
- package/template/agent/skills/mobile-developer/references/android-rules/gradle-build-performance.md +346 -0
- package/template/agent/skills/mobile-developer/references/android-rules/kotlin-concurrency-expert.md +169 -0
- package/template/agent/skills/mobile-developer/references/android-rules/rxjava-to-coroutines-migration.md +101 -0
- package/template/agent/skills/mobile-developer/references/android-rules/xml-to-compose-migration.md +338 -0
- package/template/agent/skills/mobile-developer/references/flutter-rules/dart-best-practices.md +52 -0
- package/template/agent/skills/mobile-developer/references/flutter-rules/dart-checks-migration.md +134 -0
- package/template/agent/skills/mobile-developer/references/flutter-rules/dart-cli-app-best-practices.md +123 -0
- package/template/agent/skills/mobile-developer/references/flutter-rules/dart-doc-validation.md +45 -0
- package/template/agent/skills/mobile-developer/references/flutter-rules/dart-matcher-best-practices.md +106 -0
- package/template/agent/skills/mobile-developer/references/flutter-rules/dart-modern-features.md +241 -0
- package/template/agent/skills/mobile-developer/references/flutter-rules/dart-package-maintenance.md +75 -0
- package/template/agent/skills/mobile-developer/references/flutter-rules/dart-test-fundamentals.md +124 -0
- package/template/agent/skills/mobile-developer/references/flutter.md +291 -0
- package/template/agent/skills/mobile-developer/references/ios-native.md +358 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/accessibility-patterns.md +215 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/animation-advanced.md +403 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/animation-basics.md +284 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/animation-transitions.md +326 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/charts-accessibility.md +135 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/charts.md +602 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/image-optimization.md +203 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/latest-apis.md +464 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/layout-best-practices.md +266 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/liquid-glass.md +416 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/list-patterns.md +394 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/macos-scenes.md +318 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/macos-views.md +357 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/macos-window-styling.md +303 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/performance-patterns.md +403 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/scroll-patterns.md +293 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/sheet-navigation-patterns.md +363 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/state-management.md +417 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/view-structure.md +389 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/_sections.md +86 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/_template.md +28 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/animation-derived-value.md +53 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/animation-gesture-detector-press.md +95 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/animation-gpu-properties.md +65 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/design-system-compound-components.md +66 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/fonts-config-plugin.md +71 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/imports-design-system-folder.md +68 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/js-hoist-intl.md +61 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/list-performance-callbacks.md +44 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/list-performance-function-references.md +132 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/list-performance-images.md +53 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/list-performance-inline-objects.md +97 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/list-performance-item-expensive.md +94 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/list-performance-item-memo.md +82 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/list-performance-item-types.md +104 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/list-performance-virtualize.md +67 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/monorepo-native-deps-in-app.md +46 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/monorepo-single-dependency-versions.md +63 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/navigation-native-navigators.md +188 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/react-compiler-destructure-functions.md +50 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/react-compiler-reanimated-shared-values.md +48 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/react-state-dispatcher.md +91 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/react-state-fallback.md +56 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/react-state-minimize.md +65 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/rendering-no-falsy-and.md +74 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/rendering-text-in-text-component.md +36 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/scroll-position-no-state.md +82 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/state-ground-truth.md +80 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-expo-image.md +66 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-image-gallery.md +104 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-measure-views.md +78 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-menus.md +174 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-native-modals.md +77 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-pressable.md +61 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-safe-area-scroll.md +65 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-scrollview-content-inset.md +45 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-styling.md +87 -0
- package/template/agent/skills/mobile-developer/references/react-native.md +345 -0
- package/template/agent/skills/monetization-strategist/SKILL.md +119 -0
- package/template/agent/skills/paid-acquisition-specialist/SKILL.md +119 -0
- package/template/agent/skills/product-manager/SKILL.md +105 -0
- package/template/agent/skills/real-time-features/SKILL.md +194 -0
- package/template/agent/skills/retention-specialist/SKILL.md +123 -0
- package/template/agent/skills/saas-architect/SKILL.md +139 -0
- package/template/agent/skills/security-engineer/SKILL.md +133 -0
- package/template/agent/skills/seo-specialist/SKILL.md +130 -0
- package/template/agent/skills/subscription-billing/SKILL.md +179 -0
- package/template/agent/skills/ux-designer/SKILL.md +128 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# SwiftUI Layout Best Practices Reference
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Relative Layout Over Constants](#relative-layout-over-constants)
|
|
6
|
+
- [Context-Agnostic Views](#context-agnostic-views)
|
|
7
|
+
- [Own Your Container](#own-your-container)
|
|
8
|
+
- [Layout Performance](#layout-performance)
|
|
9
|
+
- [View Logic and Testability](#view-logic-and-testability)
|
|
10
|
+
- [Full-Width Views](#full-width-views)
|
|
11
|
+
- [Action Handlers](#action-handlers)
|
|
12
|
+
- [Summary Checklist](#summary-checklist)
|
|
13
|
+
|
|
14
|
+
## Relative Layout Over Constants
|
|
15
|
+
|
|
16
|
+
**Use dynamic layout calculations instead of hard-coded values.**
|
|
17
|
+
|
|
18
|
+
```swift
|
|
19
|
+
// Good - relative to actual layout
|
|
20
|
+
GeometryReader { geometry in
|
|
21
|
+
VStack {
|
|
22
|
+
HeaderView()
|
|
23
|
+
.frame(height: geometry.size.height * 0.2)
|
|
24
|
+
ContentView()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Avoid - magic numbers that don't adapt
|
|
29
|
+
VStack {
|
|
30
|
+
HeaderView()
|
|
31
|
+
.frame(height: 150) // Doesn't adapt to different screens
|
|
32
|
+
ContentView()
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Why**: Hard-coded values don't account for different screen sizes, orientations, or dynamic content (like status bars during phone calls).
|
|
37
|
+
|
|
38
|
+
## Context-Agnostic Views
|
|
39
|
+
|
|
40
|
+
**Views should work in any context.** Never assume presentation style or screen size.
|
|
41
|
+
|
|
42
|
+
```swift
|
|
43
|
+
// Good - adapts to given space
|
|
44
|
+
struct ProfileCard: View {
|
|
45
|
+
let user: User
|
|
46
|
+
|
|
47
|
+
var body: some View {
|
|
48
|
+
VStack {
|
|
49
|
+
Image(user.avatar)
|
|
50
|
+
.resizable()
|
|
51
|
+
.aspectRatio(contentMode: .fit)
|
|
52
|
+
Text(user.name)
|
|
53
|
+
Spacer()
|
|
54
|
+
}
|
|
55
|
+
.padding()
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Avoid - assumes full screen
|
|
60
|
+
struct ProfileCard: View {
|
|
61
|
+
let user: User
|
|
62
|
+
|
|
63
|
+
var body: some View {
|
|
64
|
+
VStack {
|
|
65
|
+
Image(user.avatar)
|
|
66
|
+
.frame(width: UIScreen.main.bounds.width) // Wrong!
|
|
67
|
+
Text(user.name)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Why**: Views should work as full screens, modals, sheets, popovers, or embedded content.
|
|
74
|
+
|
|
75
|
+
## Own Your Container
|
|
76
|
+
|
|
77
|
+
**Custom views should own static containers but not lazy/repeatable ones.**
|
|
78
|
+
|
|
79
|
+
```swift
|
|
80
|
+
// Good - owns static container
|
|
81
|
+
struct HeaderView: View {
|
|
82
|
+
var body: some View {
|
|
83
|
+
HStack {
|
|
84
|
+
Image(systemName: "star")
|
|
85
|
+
Text("Title")
|
|
86
|
+
Spacer()
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Avoid - missing container
|
|
92
|
+
struct HeaderView: View {
|
|
93
|
+
var body: some View {
|
|
94
|
+
Image(systemName: "star")
|
|
95
|
+
Text("Title")
|
|
96
|
+
// Caller must wrap in HStack
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Good - caller owns lazy container
|
|
101
|
+
struct FeedView: View {
|
|
102
|
+
let items: [Item]
|
|
103
|
+
|
|
104
|
+
var body: some View {
|
|
105
|
+
LazyVStack {
|
|
106
|
+
ForEach(items) { item in
|
|
107
|
+
ItemRow(item: item)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Layout Performance
|
|
115
|
+
|
|
116
|
+
### Avoid Layout Thrash
|
|
117
|
+
|
|
118
|
+
**Minimize deep view hierarchies and excessive layout dependencies.**
|
|
119
|
+
|
|
120
|
+
```swift
|
|
121
|
+
// Bad - deep nesting, excessive layout passes
|
|
122
|
+
VStack {
|
|
123
|
+
HStack {
|
|
124
|
+
VStack {
|
|
125
|
+
HStack {
|
|
126
|
+
VStack {
|
|
127
|
+
Text("Deep")
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Good - flatter hierarchy
|
|
135
|
+
VStack {
|
|
136
|
+
Text("Shallow")
|
|
137
|
+
Text("Structure")
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Avoid excessive `GeometryReader` and preference chains:**
|
|
142
|
+
|
|
143
|
+
```swift
|
|
144
|
+
// Bad - multiple geometry readers cause layout thrash
|
|
145
|
+
GeometryReader { outerGeometry in
|
|
146
|
+
VStack {
|
|
147
|
+
GeometryReader { innerGeometry in
|
|
148
|
+
// Layout recalculates multiple times
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Good - single geometry reader or use alternatives (iOS 17+)
|
|
154
|
+
containerRelativeFrame(.horizontal) { width, _ in
|
|
155
|
+
width * 0.8
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Gate frequent geometry updates:**
|
|
160
|
+
|
|
161
|
+
```swift
|
|
162
|
+
// Bad - updates on every pixel change
|
|
163
|
+
.onPreferenceChange(ViewSizeKey.self) { size in
|
|
164
|
+
currentSize = size
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Good - gate by threshold
|
|
168
|
+
.onPreferenceChange(ViewSizeKey.self) { size in
|
|
169
|
+
let difference = abs(size.width - currentSize.width)
|
|
170
|
+
if difference > 10 { // Only update if significant change
|
|
171
|
+
currentSize = size
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## View Logic and Testability
|
|
177
|
+
|
|
178
|
+
### Keep Business Logic in Services and Models
|
|
179
|
+
|
|
180
|
+
**Business logic belongs in services and models, not in views.** Views should stay simple and declarative — orchestrating UI state, not implementing business rules. This makes logic independently testable without requiring view instantiation.
|
|
181
|
+
|
|
182
|
+
> **iOS 17+**: Use `@Observable` with `@State`.
|
|
183
|
+
|
|
184
|
+
```swift
|
|
185
|
+
@Observable
|
|
186
|
+
final class AuthService {
|
|
187
|
+
var email = ""
|
|
188
|
+
var password = ""
|
|
189
|
+
var isValid: Bool {
|
|
190
|
+
!email.isEmpty && password.count >= 8
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
func login() async throws {
|
|
194
|
+
// Business logic here — testable without the view
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
struct LoginView: View {
|
|
199
|
+
@State private var authService = AuthService()
|
|
200
|
+
|
|
201
|
+
var body: some View {
|
|
202
|
+
Form {
|
|
203
|
+
TextField("Email", text: $authService.email)
|
|
204
|
+
SecureField("Password", text: $authService.password)
|
|
205
|
+
Button("Login") {
|
|
206
|
+
Task {
|
|
207
|
+
try? await authService.login()
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
.disabled(!authService.isValid)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
For iOS 16 and earlier, use `ObservableObject` with `@StateObject` -- see `state-management.md` for the legacy pattern.
|
|
217
|
+
|
|
218
|
+
Avoid embedding business logic directly in view closures (e.g., validation checks inside a `Button` action). This makes logic untestable without view instantiation.
|
|
219
|
+
|
|
220
|
+
**Note**: This is about making business logic testable, not about enforcing a specific architecture. The key is that logic lives outside views where it can be tested independently.
|
|
221
|
+
|
|
222
|
+
## Full-Width Views
|
|
223
|
+
|
|
224
|
+
**When a single view needs to fill the available width, use `.frame(maxWidth: .infinity, alignment:)` instead of wrapping it in a stack with a `Spacer`.**
|
|
225
|
+
|
|
226
|
+
```swift
|
|
227
|
+
// Good - frame modifier
|
|
228
|
+
Text("Hello")
|
|
229
|
+
.frame(maxWidth: .infinity, alignment: .leading)
|
|
230
|
+
|
|
231
|
+
// Avoid - unnecessary stack and spacer
|
|
232
|
+
HStack {
|
|
233
|
+
Text("Hello")
|
|
234
|
+
Spacer()
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**Why**: `.frame(maxWidth:alignment:)` is a single modifier that clearly communicates intent. Wrapping in an `HStack` with a `Spacer` adds an extra container to the view hierarchy for no benefit.
|
|
239
|
+
|
|
240
|
+
## Action Handlers
|
|
241
|
+
|
|
242
|
+
**Separate layout from logic.** View body should reference action methods, not contain inline logic.
|
|
243
|
+
|
|
244
|
+
```swift
|
|
245
|
+
// Good - action references method
|
|
246
|
+
Button("Publish Project", action: publishService.handlePublish)
|
|
247
|
+
|
|
248
|
+
// Avoid - multi-line logic in closure
|
|
249
|
+
Button("Publish Project") {
|
|
250
|
+
isLoading = true
|
|
251
|
+
apiService.publish(project) { result in /* ... */ }
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Summary Checklist
|
|
256
|
+
|
|
257
|
+
- [ ] Use relative layout over hard-coded constants
|
|
258
|
+
- [ ] Views work in any context (don't assume screen size)
|
|
259
|
+
- [ ] Custom views own static containers
|
|
260
|
+
- [ ] Avoid deep view hierarchies (layout thrash)
|
|
261
|
+
- [ ] Gate frequent geometry updates by thresholds
|
|
262
|
+
- [ ] Business logic kept in services and models (not in views)
|
|
263
|
+
- [ ] Action handlers reference methods, not inline logic
|
|
264
|
+
- [ ] Use `.frame(maxWidth: .infinity, alignment:)` for full-width views (not `HStack` + `Spacer`)
|
|
265
|
+
- [ ] Avoid excessive `GeometryReader` usage
|
|
266
|
+
- [ ] Use `containerRelativeFrame()` when appropriate
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
# SwiftUI Liquid Glass Reference (iOS 26+)
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Overview](#overview)
|
|
6
|
+
- [Availability](#availability)
|
|
7
|
+
- [Core APIs](#core-apis)
|
|
8
|
+
- [GlassEffectContainer](#glasseffectcontainer)
|
|
9
|
+
- [Glass Button Styles](#glass-button-styles)
|
|
10
|
+
- [Morphing Transitions](#morphing-transitions)
|
|
11
|
+
- [Modifier Order](#modifier-order)
|
|
12
|
+
- [Complete Examples](#complete-examples)
|
|
13
|
+
- [Fallback Strategies](#fallback-strategies)
|
|
14
|
+
- [Design System Notes](#design-system-notes)
|
|
15
|
+
- [Best Practices](#best-practices)
|
|
16
|
+
- [Checklist](#checklist)
|
|
17
|
+
|
|
18
|
+
## Overview
|
|
19
|
+
|
|
20
|
+
Liquid Glass is Apple's new design language introduced in iOS 26. It provides translucent, dynamic surfaces that respond to content and user interaction. This reference covers the native SwiftUI APIs for implementing Liquid Glass effects.
|
|
21
|
+
|
|
22
|
+
**Only adopt Liquid Glass when explicitly requested by the user.** Do not proactively convert existing UI to glass effects.
|
|
23
|
+
|
|
24
|
+
## Availability
|
|
25
|
+
|
|
26
|
+
All Liquid Glass APIs require iOS 26 or later. Always provide fallbacks:
|
|
27
|
+
|
|
28
|
+
```swift
|
|
29
|
+
if #available(iOS 26, *) {
|
|
30
|
+
// Liquid Glass implementation
|
|
31
|
+
} else {
|
|
32
|
+
// Fallback using materials
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Core APIs
|
|
37
|
+
|
|
38
|
+
### glassEffect Modifier
|
|
39
|
+
|
|
40
|
+
The primary modifier for applying glass effects to views:
|
|
41
|
+
|
|
42
|
+
```swift
|
|
43
|
+
.glassEffect(_ style: GlassEffectStyle = .regular, in shape: some Shape = .rect)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
#### Basic Usage
|
|
47
|
+
|
|
48
|
+
```swift
|
|
49
|
+
Text("Hello")
|
|
50
|
+
.padding()
|
|
51
|
+
.glassEffect() // Default regular style, rect shape
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
#### With Shape
|
|
55
|
+
|
|
56
|
+
```swift
|
|
57
|
+
Text("Rounded Glass")
|
|
58
|
+
.padding()
|
|
59
|
+
.glassEffect(in: .rect(cornerRadius: 16))
|
|
60
|
+
|
|
61
|
+
Image(systemName: "star")
|
|
62
|
+
.padding()
|
|
63
|
+
.glassEffect(in: .circle)
|
|
64
|
+
|
|
65
|
+
Text("Capsule")
|
|
66
|
+
.padding(.horizontal, 20)
|
|
67
|
+
.padding(.vertical, 10)
|
|
68
|
+
.glassEffect(in: .capsule)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### GlassEffectStyle
|
|
72
|
+
|
|
73
|
+
#### Prominence Levels
|
|
74
|
+
|
|
75
|
+
```swift
|
|
76
|
+
.glassEffect(.regular) // Standard glass appearance
|
|
77
|
+
.glassEffect(.prominent) // More visible, higher contrast
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### Tinting
|
|
81
|
+
|
|
82
|
+
Add color tint to the glass:
|
|
83
|
+
|
|
84
|
+
```swift
|
|
85
|
+
.glassEffect(.regular.tint(.blue))
|
|
86
|
+
.glassEffect(.prominent.tint(.red.opacity(0.3)))
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### Interactivity
|
|
90
|
+
|
|
91
|
+
Make glass respond to touch/pointer hover:
|
|
92
|
+
|
|
93
|
+
```swift
|
|
94
|
+
// Interactive glass - responds to user interaction
|
|
95
|
+
.glassEffect(.regular.interactive())
|
|
96
|
+
|
|
97
|
+
// Combined with tint
|
|
98
|
+
.glassEffect(.regular.tint(.blue).interactive())
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Important**: Only use `.interactive()` on elements that actually respond to user input (buttons, tappable views, focusable elements).
|
|
102
|
+
|
|
103
|
+
## GlassEffectContainer
|
|
104
|
+
|
|
105
|
+
Wraps multiple glass elements for proper visual grouping and spacing.
|
|
106
|
+
|
|
107
|
+
**Glass cannot sample other glass.** The glass material reflects and refracts light by sampling content from an area larger than itself. Nearby glass elements in different containers will produce inconsistent visual results because they cannot sample each other. `GlassEffectContainer` gives grouped elements a shared sampling region, ensuring a consistent appearance.
|
|
108
|
+
|
|
109
|
+
```swift
|
|
110
|
+
GlassEffectContainer {
|
|
111
|
+
HStack {
|
|
112
|
+
Button("One") { }
|
|
113
|
+
.glassEffect()
|
|
114
|
+
Button("Two") { }
|
|
115
|
+
.glassEffect()
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### With Spacing
|
|
121
|
+
|
|
122
|
+
Control the visual spacing between glass elements:
|
|
123
|
+
|
|
124
|
+
```swift
|
|
125
|
+
GlassEffectContainer(spacing: 24) {
|
|
126
|
+
HStack(spacing: 24) {
|
|
127
|
+
GlassChip(icon: "pencil")
|
|
128
|
+
GlassChip(icon: "eraser")
|
|
129
|
+
GlassChip(icon: "trash")
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Note**: The container's `spacing` parameter should match the actual spacing in your layout for proper glass effect rendering.
|
|
135
|
+
|
|
136
|
+
> Source: "Build a SwiftUI app with the new design" (WWDC25, session 323)
|
|
137
|
+
|
|
138
|
+
## Glass Button Styles
|
|
139
|
+
|
|
140
|
+
Built-in button styles for glass appearance:
|
|
141
|
+
|
|
142
|
+
```swift
|
|
143
|
+
// Standard glass button
|
|
144
|
+
Button("Action") { }
|
|
145
|
+
.buttonStyle(.glass)
|
|
146
|
+
|
|
147
|
+
// Prominent glass button (higher visibility)
|
|
148
|
+
Button("Primary Action") { }
|
|
149
|
+
.buttonStyle(.glassProminent)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Custom Glass Buttons
|
|
153
|
+
|
|
154
|
+
For more control, apply glass effect manually:
|
|
155
|
+
|
|
156
|
+
```swift
|
|
157
|
+
Button(action: { }) {
|
|
158
|
+
Label("Settings", systemImage: "gear")
|
|
159
|
+
.padding()
|
|
160
|
+
}
|
|
161
|
+
.glassEffect(.regular.interactive(), in: .capsule)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Morphing Transitions
|
|
165
|
+
|
|
166
|
+
Create smooth transitions between glass elements using `glassEffectID` and `@Namespace`:
|
|
167
|
+
|
|
168
|
+
```swift
|
|
169
|
+
struct MorphingExample: View {
|
|
170
|
+
@Namespace private var animation
|
|
171
|
+
@State private var isExpanded = false
|
|
172
|
+
|
|
173
|
+
var body: some View {
|
|
174
|
+
GlassEffectContainer {
|
|
175
|
+
if isExpanded {
|
|
176
|
+
ExpandedCard()
|
|
177
|
+
.glassEffect()
|
|
178
|
+
.glassEffectID("card", in: animation)
|
|
179
|
+
} else {
|
|
180
|
+
CompactCard()
|
|
181
|
+
.glassEffect()
|
|
182
|
+
.glassEffectID("card", in: animation)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
.animation(.smooth, value: isExpanded)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Requirements for Morphing
|
|
191
|
+
|
|
192
|
+
1. Both views must have the same `glassEffectID`
|
|
193
|
+
2. Use the same `@Namespace`
|
|
194
|
+
3. Wrap in `GlassEffectContainer`
|
|
195
|
+
4. Apply animation to the container or parent
|
|
196
|
+
|
|
197
|
+
## Modifier Order
|
|
198
|
+
|
|
199
|
+
**Critical**: Apply `glassEffect` after layout and visual modifiers:
|
|
200
|
+
|
|
201
|
+
```swift
|
|
202
|
+
// CORRECT order
|
|
203
|
+
Text("Label")
|
|
204
|
+
.font(.headline) // 1. Typography
|
|
205
|
+
.foregroundStyle(.primary) // 2. Color
|
|
206
|
+
.padding() // 3. Layout
|
|
207
|
+
.glassEffect() // 4. Glass effect LAST
|
|
208
|
+
|
|
209
|
+
// WRONG order - glass applied too early
|
|
210
|
+
Text("Label")
|
|
211
|
+
.glassEffect() // Wrong position
|
|
212
|
+
.padding()
|
|
213
|
+
.font(.headline)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Complete Examples
|
|
217
|
+
|
|
218
|
+
### Toolbar with Glass Buttons
|
|
219
|
+
|
|
220
|
+
```swift
|
|
221
|
+
struct GlassToolbar: View {
|
|
222
|
+
var body: some View {
|
|
223
|
+
if #available(iOS 26, *) {
|
|
224
|
+
GlassEffectContainer(spacing: 16) {
|
|
225
|
+
HStack(spacing: 16) {
|
|
226
|
+
ToolbarButton(icon: "pencil", action: { })
|
|
227
|
+
ToolbarButton(icon: "eraser", action: { })
|
|
228
|
+
ToolbarButton(icon: "scissors", action: { })
|
|
229
|
+
Spacer()
|
|
230
|
+
ToolbarButton(icon: "square.and.arrow.up", action: { })
|
|
231
|
+
}
|
|
232
|
+
.padding(.horizontal)
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
// Fallback toolbar
|
|
236
|
+
HStack(spacing: 16) {
|
|
237
|
+
// ... fallback implementation
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
struct ToolbarButton: View {
|
|
244
|
+
let icon: String
|
|
245
|
+
let action: () -> Void
|
|
246
|
+
|
|
247
|
+
var body: some View {
|
|
248
|
+
Button(action: action) {
|
|
249
|
+
Image(systemName: icon)
|
|
250
|
+
.font(.title2)
|
|
251
|
+
.frame(width: 44, height: 44)
|
|
252
|
+
}
|
|
253
|
+
.glassEffect(.regular.interactive(), in: .circle)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Card with Glass Effect
|
|
259
|
+
|
|
260
|
+
```swift
|
|
261
|
+
struct GlassCard: View {
|
|
262
|
+
let title: String
|
|
263
|
+
let subtitle: String
|
|
264
|
+
|
|
265
|
+
var body: some View {
|
|
266
|
+
if #available(iOS 26, *) {
|
|
267
|
+
cardContent
|
|
268
|
+
.glassEffect(.regular, in: .rect(cornerRadius: 20))
|
|
269
|
+
} else {
|
|
270
|
+
cardContent
|
|
271
|
+
.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 20))
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
private var cardContent: some View {
|
|
276
|
+
VStack(alignment: .leading, spacing: 8) {
|
|
277
|
+
Text(title)
|
|
278
|
+
.font(.headline)
|
|
279
|
+
Text(subtitle)
|
|
280
|
+
.font(.subheadline)
|
|
281
|
+
.foregroundStyle(.secondary)
|
|
282
|
+
}
|
|
283
|
+
.padding()
|
|
284
|
+
.frame(maxWidth: .infinity, alignment: .leading)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Segmented Control
|
|
290
|
+
|
|
291
|
+
```swift
|
|
292
|
+
struct GlassSegmentedControl: View {
|
|
293
|
+
@Binding var selection: Int
|
|
294
|
+
let options: [String]
|
|
295
|
+
@Namespace private var animation
|
|
296
|
+
|
|
297
|
+
var body: some View {
|
|
298
|
+
if #available(iOS 26, *) {
|
|
299
|
+
GlassEffectContainer(spacing: 4) {
|
|
300
|
+
HStack(spacing: 4) {
|
|
301
|
+
ForEach(options.indices, id: \.self) { index in
|
|
302
|
+
Button(options[index]) {
|
|
303
|
+
withAnimation(.smooth) {
|
|
304
|
+
selection = index
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
.padding(.horizontal, 16)
|
|
308
|
+
.padding(.vertical, 8)
|
|
309
|
+
.glassEffect(
|
|
310
|
+
selection == index ? .prominent.interactive() : .regular.interactive(),
|
|
311
|
+
in: .capsule
|
|
312
|
+
)
|
|
313
|
+
.glassEffectID(selection == index ? "selected" : "option\(index)", in: animation)
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
.padding(4)
|
|
317
|
+
}
|
|
318
|
+
} else {
|
|
319
|
+
Picker("Options", selection: $selection) {
|
|
320
|
+
ForEach(options.indices, id: \.self) { index in
|
|
321
|
+
Text(options[index]).tag(index)
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
.pickerStyle(.segmented)
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Fallback Strategies
|
|
331
|
+
|
|
332
|
+
### Using Materials
|
|
333
|
+
|
|
334
|
+
```swift
|
|
335
|
+
if #available(iOS 26, *) {
|
|
336
|
+
content.glassEffect()
|
|
337
|
+
} else {
|
|
338
|
+
content.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 16))
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Available Materials for Fallback
|
|
343
|
+
|
|
344
|
+
- `.ultraThinMaterial` - Closest to glass appearance
|
|
345
|
+
- `.thinMaterial` - Slightly more opaque
|
|
346
|
+
- `.regularMaterial` - Standard blur
|
|
347
|
+
- `.thickMaterial` - More opaque
|
|
348
|
+
- `.ultraThickMaterial` - Most opaque
|
|
349
|
+
|
|
350
|
+
### Conditional Modifier Extension
|
|
351
|
+
|
|
352
|
+
```swift
|
|
353
|
+
extension View {
|
|
354
|
+
@ViewBuilder
|
|
355
|
+
func glassEffectWithFallback(
|
|
356
|
+
_ style: GlassEffectStyle = .regular,
|
|
357
|
+
in shape: some Shape = .rect,
|
|
358
|
+
fallbackMaterial: Material = .ultraThinMaterial
|
|
359
|
+
) -> some View {
|
|
360
|
+
if #available(iOS 26, *) {
|
|
361
|
+
self.glassEffect(style, in: shape)
|
|
362
|
+
} else {
|
|
363
|
+
self.background(fallbackMaterial, in: shape)
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## Design System Notes
|
|
370
|
+
|
|
371
|
+
### Toolbar Icons
|
|
372
|
+
|
|
373
|
+
In the new design, toolbar icons use **monochrome rendering** by default. The monochrome palette reduces visual noise and maintains legibility. Use `tint(_:)` only to convey meaning (e.g., a call to action), not for visual effect.
|
|
374
|
+
|
|
375
|
+
### Sheet Presentations
|
|
376
|
+
|
|
377
|
+
Partial-height sheets use a Liquid Glass background by default. If you previously used `presentationBackground(_:)` with a custom background, consider removing it to let the new material shine. Sheets can morph out of the glass controls that present them using `navigationZoomTransition`.
|
|
378
|
+
|
|
379
|
+
### Scroll Edge Effects
|
|
380
|
+
|
|
381
|
+
An automatic scroll edge effect blurs and fades content under system toolbars to keep controls legible. Remove any custom background-darkening effects behind bar items, as they will interfere.
|
|
382
|
+
|
|
383
|
+
> Source: "Build a SwiftUI app with the new design" (WWDC25, session 323)
|
|
384
|
+
|
|
385
|
+
## Best Practices
|
|
386
|
+
|
|
387
|
+
### Do
|
|
388
|
+
|
|
389
|
+
- Use `GlassEffectContainer` for grouped glass elements (glass cannot sample other glass)
|
|
390
|
+
- Apply glass after layout modifiers
|
|
391
|
+
- Use `.interactive()` only on tappable elements
|
|
392
|
+
- Match container spacing with layout spacing
|
|
393
|
+
- Provide material-based fallbacks for older iOS
|
|
394
|
+
- Keep glass shapes consistent within a feature
|
|
395
|
+
- Remove custom `presentationBackground(_:)` on sheets to use the default glass material
|
|
396
|
+
|
|
397
|
+
### Don't
|
|
398
|
+
|
|
399
|
+
- Apply glass to every element (use sparingly)
|
|
400
|
+
- Use `.interactive()` on static content
|
|
401
|
+
- Mix different corner radii arbitrarily
|
|
402
|
+
- Forget iOS version checks
|
|
403
|
+
- Apply glass before padding/frame modifiers
|
|
404
|
+
- Nest `GlassEffectContainer` unnecessarily
|
|
405
|
+
- Add custom darkening backgrounds behind toolbars (conflicts with scroll edge effect)
|
|
406
|
+
|
|
407
|
+
## Checklist
|
|
408
|
+
|
|
409
|
+
- [ ] `#available(iOS 26, *)` with fallback
|
|
410
|
+
- [ ] `GlassEffectContainer` wraps grouped elements
|
|
411
|
+
- [ ] `.glassEffect()` applied after layout modifiers
|
|
412
|
+
- [ ] `.interactive()` only on user-interactable elements
|
|
413
|
+
- [ ] `glassEffectID` with `@Namespace` for morphing
|
|
414
|
+
- [ ] Consistent shapes and spacing across feature
|
|
415
|
+
- [ ] Container spacing matches layout spacing
|
|
416
|
+
- [ ] Appropriate prominence levels used
|