@devo-bmad-custom/agent-orchestration 1.0.2 → 1.0.4
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/lib/installer.js +33 -0
- package/package.json +1 -1
- package/src/.agents/skills/audit-website/README.md +20 -20
- package/src/.agents/skills/audit-website/SKILL.md +470 -470
- package/src/.agents/skills/audit-website/agents/openai.yaml +6 -6
- package/src/.agents/skills/audit-website/assets/icon-small.svg +41 -41
- package/src/.agents/skills/audit-website/references/OUTPUT-FORMAT.md +250 -250
- package/src/.agents/skills/clean-code-standards/SKILL.md +104 -104
- package/src/.agents/skills/excalidraw-dark-standard/SKILL.md +281 -281
- package/src/.agents/skills/frontend-responsive-design-standards/SKILL.md +434 -434
- package/src/.agents/skills/java-fundamentals/SKILL.md +116 -116
- package/src/.agents/skills/java-performance/SKILL.md +119 -119
- package/src/.agents/skills/next-best-practices/SKILL.md +153 -153
- package/src/.agents/skills/next-best-practices/async-patterns.md +87 -87
- package/src/.agents/skills/next-best-practices/bundling.md +180 -180
- package/src/.agents/skills/next-best-practices/data-patterns.md +297 -297
- package/src/.agents/skills/next-best-practices/debug-tricks.md +105 -105
- package/src/.agents/skills/next-best-practices/directives.md +73 -73
- package/src/.agents/skills/next-best-practices/error-handling.md +227 -227
- package/src/.agents/skills/next-best-practices/file-conventions.md +140 -140
- package/src/.agents/skills/next-best-practices/font.md +245 -245
- package/src/.agents/skills/next-best-practices/functions.md +108 -108
- package/src/.agents/skills/next-best-practices/hydration-error.md +91 -91
- package/src/.agents/skills/next-best-practices/image.md +173 -173
- package/src/.agents/skills/next-best-practices/metadata.md +301 -301
- package/src/.agents/skills/next-best-practices/parallel-routes.md +287 -287
- package/src/.agents/skills/next-best-practices/route-handlers.md +146 -146
- package/src/.agents/skills/next-best-practices/rsc-boundaries.md +159 -159
- package/src/.agents/skills/next-best-practices/runtime-selection.md +39 -39
- package/src/.agents/skills/next-best-practices/scripts.md +141 -141
- package/src/.agents/skills/next-best-practices/self-hosting.md +371 -371
- package/src/.agents/skills/next-best-practices/suspense-boundaries.md +67 -67
- package/src/.agents/skills/nextjs-app-router-patterns/SKILL.md +537 -537
- package/src/.agents/skills/postgresql-optimization/SKILL.md +404 -404
- package/src/.agents/skills/python-backend/SKILL.md +153 -153
- package/src/.agents/skills/python-fundamentals/SKILL.md +234 -234
- package/src/.agents/skills/python-performance/SKILL.md +404 -404
- package/src/.agents/skills/react-expert/SKILL.md +335 -335
- package/src/.agents/skills/redis-best-practices/SKILL.md +438 -438
- package/src/.agents/skills/security-best-practices/SKILL.md +288 -288
- package/src/.agents/skills/security-review/LICENSE +22 -22
- package/src/.agents/skills/security-review/SKILL.md +312 -312
- package/src/.agents/skills/security-review/infrastructure/docker.md +432 -432
- package/src/.agents/skills/security-review/languages/javascript.md +388 -388
- package/src/.agents/skills/security-review/languages/python.md +363 -363
- package/src/.agents/skills/security-review/references/api-security.md +519 -519
- package/src/.agents/skills/security-review/references/authentication.md +353 -353
- package/src/.agents/skills/security-review/references/authorization.md +372 -372
- package/src/.agents/skills/security-review/references/business-logic.md +443 -443
- package/src/.agents/skills/security-review/references/cryptography.md +329 -329
- package/src/.agents/skills/security-review/references/csrf.md +398 -398
- package/src/.agents/skills/security-review/references/data-protection.md +378 -378
- package/src/.agents/skills/security-review/references/deserialization.md +410 -410
- package/src/.agents/skills/security-review/references/error-handling.md +436 -436
- package/src/.agents/skills/security-review/references/file-security.md +457 -457
- package/src/.agents/skills/security-review/references/injection.md +259 -259
- package/src/.agents/skills/security-review/references/logging.md +433 -433
- package/src/.agents/skills/security-review/references/misconfiguration.md +435 -435
- package/src/.agents/skills/security-review/references/modern-threats.md +475 -475
- package/src/.agents/skills/security-review/references/ssrf.md +415 -415
- package/src/.agents/skills/security-review/references/supply-chain.md +405 -405
- package/src/.agents/skills/security-review/references/xss.md +336 -336
- package/src/.agents/skills/subagent-driven-development/SKILL.md +275 -275
- package/src/.agents/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -26
- package/src/.agents/skills/subagent-driven-development/implementer-prompt.md +113 -113
- package/src/.agents/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -61
- package/src/.agents/skills/systematic-debugging/CREATION-LOG.md +119 -119
- package/src/.agents/skills/systematic-debugging/SKILL.md +296 -296
- package/src/.agents/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
- package/src/.agents/skills/systematic-debugging/condition-based-waiting.md +115 -115
- package/src/.agents/skills/systematic-debugging/defense-in-depth.md +122 -122
- package/src/.agents/skills/systematic-debugging/root-cause-tracing.md +169 -169
- package/src/.agents/skills/systematic-debugging/test-academic.md +14 -14
- package/src/.agents/skills/systematic-debugging/test-pressure-1.md +58 -58
- package/src/.agents/skills/systematic-debugging/test-pressure-2.md +68 -68
- package/src/.agents/skills/systematic-debugging/test-pressure-3.md +69 -69
- package/src/.agents/skills/typescript-best-practices/SKILL.md +373 -373
- package/src/.agents/skills/ui-ux-pro-custom/SKILL.md +348 -348
- package/src/.agents/skills/ui-ux-pro-custom/data/charts.csv +26 -26
- package/src/.agents/skills/ui-ux-pro-custom/data/colors.csv +97 -97
- package/src/.agents/skills/ui-ux-pro-custom/data/icons.csv +101 -101
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/SKILL.md +106 -106
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/accessibility.md +475 -475
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/animation.md +466 -466
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/composition-locals.md +231 -231
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/deprecated-patterns.md +323 -323
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/lists-scrolling.md +400 -400
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/modifiers.md +331 -331
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/navigation.md +416 -416
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/performance.md +446 -446
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/side-effects.md +516 -516
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/foundation-source.md +13327 -13327
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/material3-source.md +19097 -19097
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/navigation-source.md +2947 -2947
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/runtime-source.md +11316 -11316
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/ui-source.md +7896 -7896
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/state-management.md +377 -377
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/styles-experimental.md +470 -470
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/theming-material3.md +349 -349
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/view-composition.md +595 -595
- package/src/.agents/skills/ui-ux-pro-custom/data/landing.csv +31 -31
- package/src/.agents/skills/ui-ux-pro-custom/data/mobile-ui-layout.md +654 -654
- package/src/.agents/skills/ui-ux-pro-custom/data/products.csv +96 -96
- package/src/.agents/skills/ui-ux-pro-custom/data/react-performance.csv +45 -45
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/astro.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/flutter.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/html-tailwind.csv +56 -56
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/jetpack-compose.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nextjs.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nuxt-ui.csv +51 -51
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nuxtjs.csv +59 -59
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/react-native.csv +56 -56
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/react.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/shadcn.csv +61 -61
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/svelte.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/swiftui.csv +51 -51
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/vue.csv +50 -50
- package/src/.agents/skills/ui-ux-pro-custom/data/styles.csv +68 -68
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/alarmkit/SKILL.md +438 -438
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/alarmkit/references/alarmkit-patterns.md +584 -584
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-clips/SKILL.md +436 -436
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-intents/SKILL.md +489 -489
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-intents/references/appintents-advanced.md +1076 -1076
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/SKILL.md +340 -340
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/references/privacy-manifest.md +90 -90
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/references/review-checklists.md +106 -106
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/coreml-conversion.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/coreml-optimization.md +344 -344
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/foundation-models.md +508 -508
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/mlx-swift.md +285 -285
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/authentication/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/authentication/references/keychain-biometric.md +211 -211
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/background-processing/SKILL.md +499 -499
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/background-processing/references/background-task-patterns.md +390 -390
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/callkit-voip/SKILL.md +461 -461
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/callkit-voip/references/callkit-patterns.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/cloudkit-sync/SKILL.md +492 -492
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/cloudkit-sync/references/cloudkit-patterns.md +461 -461
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/codable-patterns/SKILL.md +467 -467
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/contacts-framework/SKILL.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/contacts-framework/references/contacts-patterns.md +409 -409
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-bluetooth/SKILL.md +491 -491
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-bluetooth/references/ble-patterns.md +435 -435
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-motion/SKILL.md +388 -388
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-motion/references/motion-patterns.md +405 -405
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-nfc/SKILL.md +495 -495
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-nfc/references/nfc-patterns.md +420 -420
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/coreml/SKILL.md +459 -459
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/coreml/references/coreml-swift-integration.md +765 -765
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/SKILL.md +422 -422
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/references/instruments-guide.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/references/lldb-patterns.md +298 -298
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/device-integrity/SKILL.md +477 -477
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/energykit/SKILL.md +460 -460
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/energykit/references/energykit-patterns.md +541 -541
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/eventkit-calendar/SKILL.md +483 -483
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/eventkit-calendar/references/eventkit-patterns.md +326 -326
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/healthkit/SKILL.md +498 -498
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/healthkit/references/healthkit-patterns.md +602 -602
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/homekit-matter/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/homekit-matter/references/matter-commissioning.md +455 -455
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-accessibility/SKILL.md +301 -301
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-accessibility/references/a11y-patterns.md +140 -140
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/SKILL.md +418 -418
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/references/formatstyle-locale.md +627 -627
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/references/string-catalogs.md +462 -462
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/SKILL.md +441 -441
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/background-websocket.md +862 -862
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/lightweight-clients.md +93 -93
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/network-framework.md +563 -563
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/urlsession-patterns.md +1116 -1116
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/app-review-guidelines.md +174 -174
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/cryptokit-advanced.md +296 -296
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/file-storage-patterns.md +354 -354
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/privacy-manifest.md +117 -117
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/live-activities/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/live-activities/references/live-activity-patterns.md +868 -868
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/SKILL.md +485 -485
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/references/corelocation-patterns.md +730 -730
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/references/mapkit-patterns.md +748 -748
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/metrickit-diagnostics/SKILL.md +479 -479
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/musickit-audio/SKILL.md +395 -395
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/musickit-audio/references/musickit-patterns.md +363 -363
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/natural-language/SKILL.md +412 -412
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/natural-language/references/translation-patterns.md +311 -311
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/passkit-wallet/SKILL.md +398 -398
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/passkit-wallet/references/wallet-passes.md +254 -254
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/SKILL.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/references/paperkit-integration.md +376 -376
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/references/pencilkit-patterns.md +302 -302
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/permissionkit/SKILL.md +446 -446
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/permissionkit/references/permissionkit-patterns.md +435 -435
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/av-playback.md +701 -701
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/camera-capture.md +774 -774
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/image-loading-caching.md +869 -869
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/photospicker-patterns.md +597 -597
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/references/notification-patterns.md +677 -677
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/references/rich-notifications.md +745 -745
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/realitykit-ar/SKILL.md +479 -479
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/realitykit-ar/references/realitykit-patterns.md +480 -480
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/shareplay-activities/SKILL.md +483 -483
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/shareplay-activities/references/shareplay-patterns.md +544 -544
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/speech-recognition/SKILL.md +485 -485
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/SKILL.md +478 -478
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/references/app-review-guidelines.md +58 -58
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/references/storekit-advanced.md +755 -755
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-charts/SKILL.md +487 -487
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-charts/references/charts-patterns.md +895 -895
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/SKILL.md +408 -408
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/approachable-concurrency.md +80 -80
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/swift-6-2-concurrency.md +233 -233
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/swiftui-concurrency.md +187 -187
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/synchronization-primitives.md +341 -341
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-language/SKILL.md +498 -498
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-language/references/swift-patterns-extended.md +505 -505
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-testing/SKILL.md +467 -467
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-testing/references/testing-patterns.md +504 -504
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/SKILL.md +334 -334
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/core-data-coexistence.md +504 -504
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/swiftdata-advanced.md +975 -975
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/swiftdata-queries.md +675 -675
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/SKILL.md +481 -481
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/references/animation-advanced.md +804 -804
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/references/core-animation-bridge.md +553 -553
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-gestures/SKILL.md +450 -450
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-gestures/references/gesture-patterns.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/SKILL.md +336 -336
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/form.md +97 -97
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/grids.md +69 -69
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/list.md +99 -99
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/scrollview.md +147 -147
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-liquid-glass/SKILL.md +325 -325
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-liquid-glass/references/liquid-glass.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/SKILL.md +262 -262
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/deeplinks.md +207 -207
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/navigationstack.md +177 -177
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/sheets.md +169 -169
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/tabview.md +178 -178
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/SKILL.md +381 -381
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/architecture-patterns.md +486 -486
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/deprecated-migration.md +1097 -1097
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/design-polish.md +780 -780
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/platform-and-sharing.md +696 -696
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/SKILL.md +491 -491
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +46 -46
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +29 -29
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/understanding-hangs-in-your-app.md +33 -33
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/understanding-improving-swiftui-performance.md +52 -52
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/SKILL.md +428 -428
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/references/hosting-migration.md +534 -534
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/references/representable-recipes.md +1133 -1133
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/tipkit/SKILL.md +494 -494
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/tipkit/references/tipkit-patterns.md +782 -782
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/SKILL.md +475 -475
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/vision-requests.md +736 -736
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/visionkit-scanner.md +738 -738
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/SKILL.md +410 -410
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/references/weatherkit-patterns.md +567 -567
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/SKILL.md +497 -497
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/references/widgetkit-advanced.md +871 -871
- package/src/.agents/skills/ui-ux-pro-custom/data/typography.csv +57 -57
- package/src/.agents/skills/ui-ux-pro-custom/data/ui-reasoning.csv +101 -101
- package/src/.agents/skills/ui-ux-pro-custom/data/ux-guidelines.csv +99 -99
- package/src/.agents/skills/ui-ux-pro-custom/data/web-interface.csv +31 -31
- package/src/.agents/skills/ui-ux-pro-custom/scripts/core.py +253 -253
- package/src/.agents/skills/ui-ux-pro-custom/scripts/design_system.py +1067 -1067
- package/src/.agents/skills/ui-ux-pro-custom/scripts/search.py +114 -114
- package/src/.agents/skills/ux-audit/SKILL.md +150 -150
- package/src/.agents/skills/websocket-engineer/SKILL.md +168 -168
- package/src/.agents/skills/websocket-engineer/references/alternatives.md +391 -391
- package/src/.agents/skills/websocket-engineer/references/patterns.md +400 -400
- package/src/.agents/skills/websocket-engineer/references/protocol.md +195 -195
- package/src/.agents/skills/websocket-engineer/references/scaling.md +333 -333
- package/src/.agents/skills/websocket-engineer/references/security.md +474 -474
- package/src/.agents/skills/writing-skills/SKILL.md +655 -655
- package/src/.agents/skills/writing-skills/anthropic-best-practices.md +1150 -1150
- package/src/.agents/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -189
- package/src/.agents/skills/writing-skills/graphviz-conventions.dot +171 -171
- package/src/.agents/skills/writing-skills/persuasion-principles.md +187 -187
- package/src/.agents/skills/writing-skills/render-graphs.js +168 -168
- package/src/.agents/skills/writing-skills/testing-skills-with-subagents.md +384 -384
- package/src/.claude/commands/master-orchestrator.md +15 -0
- package/src/_memory/config.yaml +11 -11
- package/src/_memory/master-orchestrator-sidecar/instructions.md +97 -47
- package/src/_memory/skills/nimbalyst-tracking/SKILL.md +103 -103
- package/src/_memory/skills/writing-skills/SKILL.md +655 -655
- package/src/bmb/agents/agent-builder.md +59 -59
- package/src/bmb/agents/module-builder.md +60 -60
- package/src/bmb/agents/workflow-builder.md +61 -61
- package/src/bmb/config.yaml +12 -12
- package/src/bmb/module-help.csv +13 -13
- package/src/bmb/workflows/agent/data/agent-architecture.md +258 -258
- package/src/bmb/workflows/agent/data/agent-compilation.md +185 -185
- package/src/bmb/workflows/agent/data/agent-menu-patterns.md +189 -189
- package/src/bmb/workflows/agent/data/agent-metadata.md +133 -133
- package/src/bmb/workflows/agent/data/agent-validation.md +111 -111
- package/src/bmb/workflows/agent/data/brainstorm-context.md +96 -96
- package/src/bmb/workflows/agent/data/communication-presets.csv +61 -61
- package/src/bmb/workflows/agent/data/critical-actions.md +75 -75
- package/src/bmb/workflows/agent/data/persona-properties.md +252 -252
- package/src/bmb/workflows/agent/data/principles-crafting.md +142 -142
- package/src/bmb/workflows/agent/data/reference/module-examples/architect.md +68 -68
- package/src/bmb/workflows/agent/data/reference/with-sidecar/journal-keeper/journal-keeper-sidecar/entries/yy-mm-dd-entry-template.md +16 -16
- package/src/bmb/workflows/agent/data/understanding-agent-types.md +126 -126
- package/src/bmb/workflows/agent/steps-c/step-01-brainstorm.md +129 -129
- package/src/bmb/workflows/agent/steps-c/step-02-discovery.md +170 -170
- package/src/bmb/workflows/agent/steps-c/step-03-sidecar-metadata.md +309 -309
- package/src/bmb/workflows/agent/steps-c/step-04-persona.md +213 -213
- package/src/bmb/workflows/agent/steps-c/step-05-commands-menu.md +179 -179
- package/src/bmb/workflows/agent/steps-c/step-06-activation.md +278 -278
- package/src/bmb/workflows/agent/steps-c/step-07-build-agent.md +316 -316
- package/src/bmb/workflows/agent/steps-c/step-08-celebrate.md +247 -247
- package/src/bmb/workflows/agent/steps-e/e-01-load-existing.md +221 -221
- package/src/bmb/workflows/agent/steps-e/e-02-discover-edits.md +195 -195
- package/src/bmb/workflows/agent/steps-e/e-04-sidecar-metadata.md +126 -126
- package/src/bmb/workflows/agent/steps-e/e-05-persona.md +135 -135
- package/src/bmb/workflows/agent/steps-e/e-06-commands-menu.md +123 -123
- package/src/bmb/workflows/agent/steps-e/e-07-activation.md +124 -124
- package/src/bmb/workflows/agent/steps-e/e-08-edit-agent.md +197 -197
- package/src/bmb/workflows/agent/steps-e/e-09-celebrate.md +155 -155
- package/src/bmb/workflows/agent/steps-v/v-01-load-review.md +137 -137
- package/src/bmb/workflows/agent/steps-v/v-02a-validate-metadata.md +116 -116
- package/src/bmb/workflows/agent/steps-v/v-02b-validate-persona.md +124 -124
- package/src/bmb/workflows/agent/steps-v/v-02c-validate-menu.md +127 -127
- package/src/bmb/workflows/agent/steps-v/v-02d-validate-structure.md +134 -134
- package/src/bmb/workflows/agent/steps-v/v-02e-validate-sidecar.md +134 -134
- package/src/bmb/workflows/agent/steps-v/v-03-summary.md +104 -104
- package/src/bmb/workflows/agent/templates/agent-plan.template.md +5 -5
- package/src/bmb/workflows/agent/templates/agent-template.md +89 -89
- package/src/bmb/workflows/agent/workflow-create-agent.md +72 -72
- package/src/bmb/workflows/agent/workflow-edit-agent.md +75 -75
- package/src/bmb/workflows/agent/workflow-validate-agent.md +73 -73
- package/src/bmb/workflows/module/data/agent-architecture.md +179 -179
- package/src/bmb/workflows/module/data/agent-spec-template.md +79 -79
- package/src/bmb/workflows/module/data/module-standards.md +263 -263
- package/src/bmb/workflows/module/data/module-yaml-conventions.md +392 -392
- package/src/bmb/workflows/module/module-help-generate.md +254 -254
- package/src/bmb/workflows/module/steps-b/step-01-welcome.md +148 -148
- package/src/bmb/workflows/module/steps-b/step-02-spark.md +141 -141
- package/src/bmb/workflows/module/steps-b/step-03-module-type.md +149 -149
- package/src/bmb/workflows/module/steps-b/step-04-vision.md +83 -83
- package/src/bmb/workflows/module/steps-b/step-05-identity.md +97 -97
- package/src/bmb/workflows/module/steps-b/step-06-users.md +86 -86
- package/src/bmb/workflows/module/steps-b/step-07-value.md +76 -76
- package/src/bmb/workflows/module/steps-b/step-08-agents.md +97 -97
- package/src/bmb/workflows/module/steps-b/step-09-workflows.md +83 -83
- package/src/bmb/workflows/module/steps-b/step-10-tools.md +91 -91
- package/src/bmb/workflows/module/steps-b/step-11-scenarios.md +84 -84
- package/src/bmb/workflows/module/steps-b/step-12-creative.md +95 -95
- package/src/bmb/workflows/module/steps-b/step-13-review.md +105 -105
- package/src/bmb/workflows/module/steps-b/step-14-finalize.md +117 -117
- package/src/bmb/workflows/module/steps-c/step-01-load-brief.md +179 -179
- package/src/bmb/workflows/module/steps-c/step-01b-continue.md +82 -82
- package/src/bmb/workflows/module/steps-c/step-02-structure.md +105 -105
- package/src/bmb/workflows/module/steps-c/step-03-config.md +119 -119
- package/src/bmb/workflows/module/steps-c/step-04-agents.md +168 -168
- package/src/bmb/workflows/module/steps-c/step-05-workflows.md +184 -184
- package/src/bmb/workflows/module/steps-c/step-06-docs.md +401 -401
- package/src/bmb/workflows/module/steps-c/step-07-complete.md +152 -152
- package/src/bmb/workflows/module/steps-e/step-01-load-target.md +81 -81
- package/src/bmb/workflows/module/steps-e/step-02-select-edit.md +77 -77
- package/src/bmb/workflows/module/steps-e/step-03-apply-edit.md +77 -77
- package/src/bmb/workflows/module/steps-e/step-04-review.md +80 -80
- package/src/bmb/workflows/module/steps-e/step-05-confirm.md +75 -75
- package/src/bmb/workflows/module/steps-v/step-01-load-target.md +96 -96
- package/src/bmb/workflows/module/steps-v/step-02-file-structure.md +93 -93
- package/src/bmb/workflows/module/steps-v/step-03-module-yaml.md +99 -99
- package/src/bmb/workflows/module/steps-v/step-04-agent-specs.md +152 -152
- package/src/bmb/workflows/module/steps-v/step-05-workflow-specs.md +152 -152
- package/src/bmb/workflows/module/steps-v/step-06-documentation.md +143 -143
- package/src/bmb/workflows/module/steps-v/step-07-installation.md +102 -102
- package/src/bmb/workflows/module/steps-v/step-08-report.md +197 -197
- package/src/bmb/workflows/module/templates/brief-template.md +154 -154
- package/src/bmb/workflows/module/templates/workflow-spec-template.md +96 -96
- package/src/bmb/workflows/module/workflow-create-module-brief.md +71 -71
- package/src/bmb/workflows/module/workflow-create-module.md +86 -86
- package/src/bmb/workflows/module/workflow-edit-module.md +66 -66
- package/src/bmb/workflows/module/workflow-validate-module.md +66 -66
- package/src/bmb/workflows/workflow/data/architecture.md +150 -150
- package/src/bmb/workflows/workflow/data/common-workflow-tools.csv +19 -19
- package/src/bmb/workflows/workflow/data/csv-data-file-standards.md +53 -53
- package/src/bmb/workflows/workflow/data/frontmatter-standards.md +184 -184
- package/src/bmb/workflows/workflow/data/input-discovery-standards.md +191 -191
- package/src/bmb/workflows/workflow/data/intent-vs-prescriptive-spectrum.md +44 -44
- package/src/bmb/workflows/workflow/data/menu-handling-standards.md +133 -133
- package/src/bmb/workflows/workflow/data/output-format-standards.md +135 -135
- package/src/bmb/workflows/workflow/data/step-file-rules.md +235 -235
- package/src/bmb/workflows/workflow/data/step-type-patterns.md +257 -257
- package/src/bmb/workflows/workflow/data/subprocess-optimization-patterns.md +188 -188
- package/src/bmb/workflows/workflow/data/trimodal-workflow-structure.md +164 -164
- package/src/bmb/workflows/workflow/data/workflow-chaining-standards.md +222 -222
- package/src/bmb/workflows/workflow/data/workflow-examples.md +232 -232
- package/src/bmb/workflows/workflow/data/workflow-type-criteria.md +134 -134
- package/src/bmb/workflows/workflow/steps-c/step-00-conversion.md +263 -263
- package/src/bmb/workflows/workflow/steps-c/step-01-discovery.md +194 -194
- package/src/bmb/workflows/workflow/steps-c/step-01b-continuation.md +3 -3
- package/src/bmb/workflows/workflow/steps-c/step-02-classification.md +270 -270
- package/src/bmb/workflows/workflow/steps-c/step-03-requirements.md +283 -283
- package/src/bmb/workflows/workflow/steps-c/step-04-tools.md +282 -282
- package/src/bmb/workflows/workflow/steps-c/step-05-plan-review.md +243 -243
- package/src/bmb/workflows/workflow/steps-c/step-06-design.md +330 -330
- package/src/bmb/workflows/workflow/steps-c/step-07-foundation.md +239 -239
- package/src/bmb/workflows/workflow/steps-c/step-08-build-step-01.md +379 -379
- package/src/bmb/workflows/workflow/steps-c/step-09-build-next-step.md +350 -350
- package/src/bmb/workflows/workflow/steps-c/step-10-confirmation.md +322 -322
- package/src/bmb/workflows/workflow/steps-c/step-11-completion.md +191 -191
- package/src/bmb/workflows/workflow/steps-e/step-e-01-assess-workflow.md +237 -237
- package/src/bmb/workflows/workflow/steps-e/step-e-02-discover-edits.md +251 -251
- package/src/bmb/workflows/workflow/steps-e/step-e-03-fix-validation.md +254 -254
- package/src/bmb/workflows/workflow/steps-e/step-e-04-direct-edit.md +277 -277
- package/src/bmb/workflows/workflow/steps-e/step-e-05-apply-edit.md +154 -154
- package/src/bmb/workflows/workflow/steps-e/step-e-06-validate-after.md +190 -190
- package/src/bmb/workflows/workflow/steps-e/step-e-07-complete.md +206 -206
- package/src/bmb/workflows/workflow/steps-v/step-01-validate-max-mode.md +109 -109
- package/src/bmb/workflows/workflow/steps-v/step-01-validate.md +221 -221
- package/src/bmb/workflows/workflow/steps-v/step-01b-structure.md +152 -152
- package/src/bmb/workflows/workflow/steps-v/step-02-frontmatter-validation.md +199 -199
- package/src/bmb/workflows/workflow/steps-v/step-02b-path-violations.md +265 -265
- package/src/bmb/workflows/workflow/steps-v/step-03-menu-validation.md +164 -164
- package/src/bmb/workflows/workflow/steps-v/step-04-step-type-validation.md +211 -211
- package/src/bmb/workflows/workflow/steps-v/step-05-output-format-validation.md +200 -200
- package/src/bmb/workflows/workflow/steps-v/step-06-validation-design-check.md +195 -195
- package/src/bmb/workflows/workflow/steps-v/step-07-instruction-style-check.md +209 -209
- package/src/bmb/workflows/workflow/steps-v/step-08-collaborative-experience-check.md +199 -199
- package/src/bmb/workflows/workflow/steps-v/step-08b-subprocess-optimization.md +179 -179
- package/src/bmb/workflows/workflow/steps-v/step-09-cohesive-review.md +186 -186
- package/src/bmb/workflows/workflow/steps-v/step-10-report-complete.md +154 -154
- package/src/bmb/workflows/workflow/steps-v/step-11-plan-validation.md +237 -237
- package/src/bmb/workflows/workflow/templates/minimal-output-template.md +11 -11
- package/src/bmb/workflows/workflow/templates/step-01-init-continuable-template.md +241 -241
- package/src/bmb/workflows/workflow/templates/step-1b-template.md +224 -224
- package/src/bmb/workflows/workflow/templates/step-template.md +294 -294
- package/src/bmb/workflows/workflow/templates/workflow-template.md +102 -102
- package/src/bmb/workflows/workflow/workflow-create-workflow.md +79 -79
- package/src/bmb/workflows/workflow/workflow-edit-workflow.md +65 -65
- package/src/bmb/workflows/workflow/workflow-rework-workflow.md +65 -65
- package/src/bmb/workflows/workflow/workflow-validate-max-parallel-workflow.md +66 -66
- package/src/bmb/workflows/workflow/workflow-validate-workflow.md +65 -65
- package/src/bmm/agents/analyst.md +104 -104
- package/src/bmm/agents/dev.md +100 -100
- package/src/bmm/agents/qa.md +100 -90
- package/src/bmm/agents/tech-writer/tech-writer.md +94 -94
- package/src/bmm/module-help.csv +31 -31
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01-init.md +115 -115
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01b-continue.md +107 -107
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-02-vision.md +141 -141
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-03-users.md +144 -144
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-04-metrics.md +147 -147
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-05-scope.md +161 -161
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-06-complete.md +99 -99
- package/src/bmm/workflows/1-analysis/create-product-brief/workflow.md +57 -57
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-01-init.md +87 -87
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-02-domain-analysis.md +156 -156
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-03-competitive-landscape.md +165 -165
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-04-regulatory-focus.md +140 -140
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-05-technical-trends.md +152 -152
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-06-research-synthesis.md +345 -345
- package/src/bmm/workflows/1-analysis/research/market-steps/step-01-init.md +92 -92
- package/src/bmm/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +164 -164
- package/src/bmm/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +174 -174
- package/src/bmm/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +184 -184
- package/src/bmm/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +105 -105
- package/src/bmm/workflows/1-analysis/research/market-steps/step-06-research-completion.md +360 -360
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-01-init.md +87 -87
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-02-technical-overview.md +165 -165
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-03-integration-patterns.md +174 -174
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-04-architectural-patterns.md +141 -141
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-05-implementation-research.md +159 -159
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-06-research-synthesis.md +387 -387
- package/src/bmm/workflows/1-analysis/research/workflow-domain-research.md +54 -54
- package/src/bmm/workflows/1-analysis/research/workflow-market-research.md +54 -54
- package/src/bmm/workflows/1-analysis/research/workflow-technical-research.md +54 -54
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-01b-continue.md +100 -100
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02-discovery.md +160 -160
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02b-vision.md +88 -88
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02c-executive-summary.md +99 -99
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-03-success.md +169 -169
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-04-journeys.md +156 -156
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-05-domain.md +136 -136
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-06-innovation.md +176 -176
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-07-project-type.md +184 -184
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-08-scoping.md +174 -174
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-09-functional.md +175 -175
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-10-nonfunctional.md +189 -189
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-11-polish.md +162 -162
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-12-complete.md +79 -79
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01-discovery.md +183 -183
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01b-legacy-conversion.md +149 -149
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-02-review.md +187 -187
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-03-edit.md +192 -192
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-04-complete.md +108 -108
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-01-discovery.md +166 -166
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02-format-detection.md +131 -131
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02b-parity-check.md +150 -150
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-03-density-validation.md +118 -118
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-04-brief-coverage-validation.md +155 -155
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-05-measurability-validation.md +170 -170
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-06-traceability-validation.md +158 -158
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-07-implementation-leakage-validation.md +147 -147
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-08-domain-compliance-validation.md +182 -182
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-09-project-type-validation.md +202 -202
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-10-smart-validation.md +148 -148
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-11-holistic-quality-validation.md +201 -201
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-12-completeness-validation.md +179 -179
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-13-report-complete.md +164 -164
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md +65 -65
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md +65 -65
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +63 -63
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01b-continue.md +63 -63
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +106 -106
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +111 -111
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +115 -115
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +127 -127
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +167 -167
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +143 -143
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +118 -118
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +154 -154
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +136 -136
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +165 -165
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +135 -135
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +192 -192
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +101 -101
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +45 -45
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md +185 -185
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md +129 -129
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md +130 -130
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md +93 -93
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md +196 -196
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +129 -129
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +54 -54
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-01b-continue.md +82 -82
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md +106 -106
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md +138 -138
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +129 -129
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md +166 -166
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md +186 -186
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md +163 -163
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md +38 -38
- package/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +49 -49
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +124 -124
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +122 -122
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +84 -84
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +58 -58
- package/src/bmm/workflows/4-implementation/code-review/workflow.yaml +43 -43
- package/src/bmm/workflows/4-implementation/correct-course/workflow.yaml +53 -53
- package/src/bmm/workflows/4-implementation/create-story/checklist.md +159 -159
- package/src/bmm/workflows/4-implementation/create-story/template.md +79 -79
- package/src/bmm/workflows/4-implementation/create-story/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/dev-story/workflow.yaml +20 -20
- package/src/bmm/workflows/4-implementation/retrospective/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml +25 -25
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-01-mode-detection.md +158 -158
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-02-context-gathering.md +122 -122
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-03-execute.md +93 -93
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-04-self-check.md +93 -93
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-05-adversarial-review.md +87 -87
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-06-resolve-findings.md +146 -146
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +50 -50
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-02-investigate.md +152 -152
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-03-generate.md +123 -123
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +201 -201
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +79 -79
- package/src/bmm/workflows/document-project/workflow.yaml +22 -22
- package/src/bmm/workflows/generate-project-context/steps/step-01-discover.md +184 -184
- package/src/bmm/workflows/generate-project-context/steps/step-02-generate.md +322 -322
- package/src/bmm/workflows/generate-project-context/steps/step-03-complete.md +235 -235
- package/src/bmm/workflows/generate-project-context/workflow.md +49 -49
- package/src/bmm/workflows/qa/automate/workflow.yaml +233 -233
- package/src/bmm/workflows/qa-generate-e2e-tests/workflow.yaml +42 -42
- package/src/core/config.yaml +9 -9
- package/src/core/module-help.csv +10 -10
- package/src/core/scripts/generate-loop-report.py +72 -72
- package/src/core/tasks/editorial-review-prose.xml +101 -101
- package/src/core/tasks/editorial-review-structure.xml +207 -207
- package/src/core/tasks/help.md +86 -86
- package/src/core/tasks/index-docs.xml +64 -64
- package/src/core/tasks/review-adversarial-general.xml +66 -66
- package/src/core/tasks/review-adversarial-loop.xml +46 -46
- package/src/core/tasks/review-edge-case-hunter.xml +63 -63
- package/src/core/tasks/review-party-loop.xml +46 -46
- package/src/core/tasks/shard-doc.xml +107 -107
- package/src/core/tasks/workflow.xml +235 -235
- package/src/core/templates/review-loop-report.html +88 -88
- package/src/core/templates/review-loop-report.md +5 -5
- package/src/core/workflows/advanced-elicitation/workflow.xml +117 -117
- package/src/core/workflows/brainstorming/steps/step-01-session-setup.md +212 -212
- package/src/core/workflows/brainstorming/steps/step-01b-continue.md +122 -122
- package/src/core/workflows/brainstorming/steps/step-02a-user-selected.md +225 -225
- package/src/core/workflows/brainstorming/steps/step-02b-ai-recommended.md +237 -237
- package/src/core/workflows/brainstorming/steps/step-02c-random-selection.md +209 -209
- package/src/core/workflows/brainstorming/steps/step-02d-progressive-flow.md +264 -264
- package/src/core/workflows/brainstorming/steps/step-02e-deep-dive.md +68 -68
- package/src/core/workflows/brainstorming/steps/step-03-technique-execution.md +403 -403
- package/src/core/workflows/brainstorming/steps/step-04-idea-organization.md +303 -303
- package/src/core/workflows/brainstorming/workflow.md +60 -60
- package/src/core/workflows/extract-trackers/workflow.md +45 -45
- package/src/core/workflows/party-mode/steps/step-01-agent-loading.md +142 -142
- package/src/core/workflows/party-mode/workflow.md +194 -194
- package/src/docs/dev/tmux/actions_popup.py +291 -291
- package/src/docs/dev/tmux/tmux-setup.md +62 -1
|
@@ -1,804 +1,804 @@
|
|
|
1
|
-
# SwiftUI Animation Advanced Reference
|
|
2
|
-
|
|
3
|
-
Detailed API reference for SwiftUI animation types, protocols, and patterns.
|
|
4
|
-
Covers material beyond the SKILL.md summary.
|
|
5
|
-
|
|
6
|
-
## Contents
|
|
7
|
-
|
|
8
|
-
- [CustomAnimation Protocol (iOS 17+)](#customanimation-protocol-ios-17)
|
|
9
|
-
- [Spring Type -- All Initializer Variants](#spring-type-all-initializer-variants)
|
|
10
|
-
- [UnitCurve Types (iOS 17+)](#unitcurve-types-ios-17)
|
|
11
|
-
- [PhaseAnimator Deep Patterns](#phaseanimator-deep-patterns)
|
|
12
|
-
- [KeyframeAnimator Multi-Track Examples](#keyframeanimator-multi-track-examples)
|
|
13
|
-
- [Transaction and TransactionKey](#transaction-and-transactionkey)
|
|
14
|
-
- [All Transition Types (iOS 17+)](#all-transition-types-ios-17)
|
|
15
|
-
- [All Symbol Effect Types](#all-symbol-effect-types)
|
|
16
|
-
- [Reduce Motion Implementation Patterns](#reduce-motion-implementation-patterns)
|
|
17
|
-
- [Animation Performance Tips](#animation-performance-tips)
|
|
18
|
-
|
|
19
|
-
## CustomAnimation Protocol (iOS 17+)
|
|
20
|
-
|
|
21
|
-
Create entirely custom animation curves by conforming to `CustomAnimation`.
|
|
22
|
-
|
|
23
|
-
```swift
|
|
24
|
-
@preconcurrency protocol CustomAnimation: Hashable, Sendable
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
### Required Method
|
|
28
|
-
|
|
29
|
-
```swift
|
|
30
|
-
func animate<V: VectorArithmetic>(
|
|
31
|
-
value: V,
|
|
32
|
-
time: TimeInterval,
|
|
33
|
-
context: inout AnimationContext<V>
|
|
34
|
-
) -> V?
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Return the interpolated value at the given time. Return `nil` when the
|
|
38
|
-
animation is complete.
|
|
39
|
-
|
|
40
|
-
### Optional Methods
|
|
41
|
-
|
|
42
|
-
```swift
|
|
43
|
-
func velocity<V: VectorArithmetic>(
|
|
44
|
-
value: V,
|
|
45
|
-
time: TimeInterval,
|
|
46
|
-
context: AnimationContext<V>
|
|
47
|
-
) -> V?
|
|
48
|
-
|
|
49
|
-
func shouldMerge<V: VectorArithmetic>(
|
|
50
|
-
previous: Animation,
|
|
51
|
-
value: V,
|
|
52
|
-
time: TimeInterval,
|
|
53
|
-
context: inout AnimationContext<V>
|
|
54
|
-
) -> Bool
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### Full Example: Elastic Ease-In-Out
|
|
58
|
-
|
|
59
|
-
```swift
|
|
60
|
-
struct ElasticAnimation: CustomAnimation {
|
|
61
|
-
let duration: TimeInterval
|
|
62
|
-
|
|
63
|
-
func animate<V: VectorArithmetic>(
|
|
64
|
-
value: V,
|
|
65
|
-
time: TimeInterval,
|
|
66
|
-
context: inout AnimationContext<V>
|
|
67
|
-
) -> V? {
|
|
68
|
-
guard time <= duration else { return nil }
|
|
69
|
-
let p = time / duration
|
|
70
|
-
let s = sin((20 * p - 11.125) * ((2 * .pi) / 4.5))
|
|
71
|
-
let progress: Double
|
|
72
|
-
if p < 0.5 {
|
|
73
|
-
progress = -(pow(2, 20 * p - 10) * s) / 2
|
|
74
|
-
} else {
|
|
75
|
-
progress = (pow(2, -20 * p + 10) * s) / 2 + 1
|
|
76
|
-
}
|
|
77
|
-
return value.scaled(by: progress)
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Ergonomic Extension Pattern
|
|
83
|
-
|
|
84
|
-
Expose custom animations as static members on `Animation`.
|
|
85
|
-
|
|
86
|
-
```swift
|
|
87
|
-
extension Animation {
|
|
88
|
-
static var elastic: Animation {
|
|
89
|
-
elastic(duration: 0.35)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
static func elastic(duration: TimeInterval) -> Animation {
|
|
93
|
-
Animation(ElasticAnimation(duration: duration))
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Usage
|
|
98
|
-
withAnimation(.elastic(duration: 0.5)) { isActive.toggle() }
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### Supporting Types
|
|
102
|
-
|
|
103
|
-
| Type | Role |
|
|
104
|
-
|---|---|
|
|
105
|
-
| `AnimationContext<V>` | Carries environment and per-animation state |
|
|
106
|
-
| `AnimationState` | Key-value storage for persisted state |
|
|
107
|
-
| `AnimationStateKey` | Protocol for defining custom state keys |
|
|
108
|
-
|
|
109
|
-
## Spring Type -- All Initializer Variants
|
|
110
|
-
|
|
111
|
-
### Perceptual (Preferred)
|
|
112
|
-
|
|
113
|
-
```swift
|
|
114
|
-
Spring(duration: 0.5, bounce: 0.0)
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
- `duration` -- Perceptual duration controlling pace. Default `0.5`.
|
|
118
|
-
- `bounce` -- Bounciness. `0.0` = no bounce, `1.0` = undamped. Negative values
|
|
119
|
-
produce overdamped springs. Default `0.0`.
|
|
120
|
-
|
|
121
|
-
### Physical Parameters
|
|
122
|
-
|
|
123
|
-
```swift
|
|
124
|
-
Spring(mass: 1.0, stiffness: 100.0, damping: 10.0, allowOverDamping: false)
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
- `mass` -- Mass at end of spring. Default `1.0`.
|
|
128
|
-
- `stiffness` -- Spring stiffness coefficient.
|
|
129
|
-
- `damping` -- Friction-like drag force.
|
|
130
|
-
- `allowOverDamping` -- Permit damping ratio > 1. Default `false`.
|
|
131
|
-
|
|
132
|
-
### Response-Based
|
|
133
|
-
|
|
134
|
-
```swift
|
|
135
|
-
Spring(response: 0.5, dampingRatio: 0.7)
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
- `response` -- Stiffness expressed as approximate duration in seconds.
|
|
139
|
-
- `dampingRatio` -- Fraction of critical damping. `1.0` = critically damped.
|
|
140
|
-
|
|
141
|
-
### Settling-Based
|
|
142
|
-
|
|
143
|
-
```swift
|
|
144
|
-
Spring(settlingDuration: 1.0, dampingRatio: 0.8, epsilon: 0.001)
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
- `settlingDuration` -- Estimated time to come to rest.
|
|
148
|
-
- `dampingRatio` -- Fraction of critical damping.
|
|
149
|
-
- `epsilon` -- Threshold for considering the spring at rest. Default `0.001`.
|
|
150
|
-
|
|
151
|
-
### Presets
|
|
152
|
-
|
|
153
|
-
```swift
|
|
154
|
-
Spring.smooth // no bounce
|
|
155
|
-
Spring.smooth(duration: 0.5, extraBounce: 0.0)
|
|
156
|
-
Spring.snappy // small bounce
|
|
157
|
-
Spring.snappy(duration: 0.4, extraBounce: 0.1)
|
|
158
|
-
Spring.bouncy // visible bounce
|
|
159
|
-
Spring.bouncy(duration: 0.5, extraBounce: 0.2)
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### Querying State
|
|
163
|
-
|
|
164
|
-
```swift
|
|
165
|
-
let spring = Spring(duration: 0.5, bounce: 0.3)
|
|
166
|
-
let v = spring.value(target: 1.0, initialVelocity: 0.0, time: 0.25)
|
|
167
|
-
let vel = spring.velocity(target: 1.0, initialVelocity: 0.0, time: 0.25)
|
|
168
|
-
let settle = spring.settlingDuration(target: 1.0, initialVelocity: 0.0, epsilon: 0.001)
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### Parameter Conversion
|
|
172
|
-
|
|
173
|
-
```swift
|
|
174
|
-
let spring = Spring(duration: 0.5, bounce: 0.3)
|
|
175
|
-
// Access physical equivalents:
|
|
176
|
-
spring.mass // 1.0
|
|
177
|
-
spring.stiffness // 157.9
|
|
178
|
-
spring.damping // 17.6
|
|
179
|
-
spring.response
|
|
180
|
-
spring.dampingRatio
|
|
181
|
-
spring.settlingDuration
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
## UnitCurve Types (iOS 17+)
|
|
185
|
-
|
|
186
|
-
Map input progress [0,1] to output progress [0,1]. Used with
|
|
187
|
-
`.timingCurve(_:duration:)`.
|
|
188
|
-
|
|
189
|
-
### Built-in Curves
|
|
190
|
-
|
|
191
|
-
```swift
|
|
192
|
-
UnitCurve.linear
|
|
193
|
-
UnitCurve.easeIn
|
|
194
|
-
UnitCurve.easeOut
|
|
195
|
-
UnitCurve.easeInOut
|
|
196
|
-
UnitCurve.circularEaseIn
|
|
197
|
-
UnitCurve.circularEaseOut
|
|
198
|
-
UnitCurve.circularEaseInOut
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
### Custom Bezier Curve
|
|
202
|
-
|
|
203
|
-
```swift
|
|
204
|
-
UnitCurve.bezier(
|
|
205
|
-
startControlPoint: UnitPoint(x: 0.42, y: 0.0),
|
|
206
|
-
endControlPoint: UnitPoint(x: 0.58, y: 1.0)
|
|
207
|
-
)
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### Instance Members
|
|
211
|
-
|
|
212
|
-
```swift
|
|
213
|
-
let curve = UnitCurve.easeInOut
|
|
214
|
-
curve.value(at: 0.5) // output progress at midpoint
|
|
215
|
-
curve.velocity(at: 0.5) // rate of change at midpoint
|
|
216
|
-
curve.inverse // swaps x and y components
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
### Usage with Animation
|
|
220
|
-
|
|
221
|
-
```swift
|
|
222
|
-
.animation(.timingCurve(UnitCurve.circularEaseIn, duration: 0.4), value: x)
|
|
223
|
-
|
|
224
|
-
// Cubic bezier control points
|
|
225
|
-
.animation(.timingCurve(0.68, -0.55, 0.27, 1.55, duration: 0.5), value: x)
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
## PhaseAnimator Deep Patterns
|
|
229
|
-
|
|
230
|
-
### Multi-Phase with Complex State
|
|
231
|
-
|
|
232
|
-
```swift
|
|
233
|
-
enum LoadPhase: CaseIterable {
|
|
234
|
-
case ready, loading, spinning, complete
|
|
235
|
-
|
|
236
|
-
var scale: Double {
|
|
237
|
-
switch self {
|
|
238
|
-
case .ready: 1.0
|
|
239
|
-
case .loading: 0.9
|
|
240
|
-
case .spinning: 1.0
|
|
241
|
-
case .complete: 1.1
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
var rotation: Angle {
|
|
246
|
-
switch self {
|
|
247
|
-
case .spinning: .degrees(360)
|
|
248
|
-
default: .zero
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
var opacity: Double {
|
|
253
|
-
self == .loading ? 0.7 : 1.0
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
struct LoadingIndicator: View {
|
|
258
|
-
var body: some View {
|
|
259
|
-
PhaseAnimator(LoadPhase.allCases) { phase in
|
|
260
|
-
Image(systemName: "arrow.triangle.2.circlepath")
|
|
261
|
-
.font(.title)
|
|
262
|
-
.scaleEffect(phase.scale)
|
|
263
|
-
.rotationEffect(phase.rotation)
|
|
264
|
-
.opacity(phase.opacity)
|
|
265
|
-
} animation: { phase in
|
|
266
|
-
switch phase {
|
|
267
|
-
case .ready: .smooth(duration: 0.2)
|
|
268
|
-
case .loading: .easeIn(duration: 0.15)
|
|
269
|
-
case .spinning: .linear(duration: 0.6)
|
|
270
|
-
case .complete: .spring(duration: 0.3, bounce: 0.4)
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
### Trigger-Based One-Shot
|
|
278
|
-
|
|
279
|
-
Run through all phases once each time the trigger value changes.
|
|
280
|
-
|
|
281
|
-
```swift
|
|
282
|
-
struct FeedbackDot: View {
|
|
283
|
-
@State private var feedbackTrigger = 0
|
|
284
|
-
|
|
285
|
-
var body: some View {
|
|
286
|
-
Circle()
|
|
287
|
-
.frame(width: 20, height: 20)
|
|
288
|
-
.phaseAnimator(
|
|
289
|
-
[false, true, false],
|
|
290
|
-
trigger: feedbackTrigger
|
|
291
|
-
) { content, phase in
|
|
292
|
-
content.scaleEffect(phase ? 1.5 : 1.0)
|
|
293
|
-
} animation: { _ in
|
|
294
|
-
.spring(duration: 0.25, bounce: 0.5)
|
|
295
|
-
}
|
|
296
|
-
.onTapGesture { feedbackTrigger += 1 }
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
### View Modifier Form
|
|
302
|
-
|
|
303
|
-
```swift
|
|
304
|
-
Text("Hello")
|
|
305
|
-
.phaseAnimator([0.0, 1.0, 0.0]) { content, phase in
|
|
306
|
-
content.opacity(phase)
|
|
307
|
-
} animation: { _ in .easeInOut(duration: 0.8) }
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
## KeyframeAnimator Multi-Track Examples
|
|
311
|
-
|
|
312
|
-
### Bounce-and-Fade
|
|
313
|
-
|
|
314
|
-
```swift
|
|
315
|
-
struct BounceValues {
|
|
316
|
-
var yOffset: Double = 0
|
|
317
|
-
var scale: Double = 1.0
|
|
318
|
-
var opacity: Double = 1.0
|
|
319
|
-
var rotation: Angle = .zero
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
struct BouncingBadge: View {
|
|
323
|
-
@State private var trigger = false
|
|
324
|
-
|
|
325
|
-
var body: some View {
|
|
326
|
-
Text("NEW")
|
|
327
|
-
.font(.caption.bold())
|
|
328
|
-
.padding(6)
|
|
329
|
-
.background(.red, in: Capsule())
|
|
330
|
-
.keyframeAnimator(
|
|
331
|
-
initialValue: BounceValues(),
|
|
332
|
-
trigger: trigger
|
|
333
|
-
) { content, value in
|
|
334
|
-
content
|
|
335
|
-
.offset(y: value.yOffset)
|
|
336
|
-
.scaleEffect(value.scale)
|
|
337
|
-
.opacity(value.opacity)
|
|
338
|
-
.rotationEffect(value.rotation)
|
|
339
|
-
} keyframes: { _ in
|
|
340
|
-
KeyframeTrack(\.yOffset) {
|
|
341
|
-
SpringKeyframe(-20, duration: 0.2)
|
|
342
|
-
CubicKeyframe(5, duration: 0.15)
|
|
343
|
-
SpringKeyframe(0, duration: 0.3)
|
|
344
|
-
}
|
|
345
|
-
KeyframeTrack(\.scale) {
|
|
346
|
-
CubicKeyframe(1.3, duration: 0.2)
|
|
347
|
-
CubicKeyframe(0.95, duration: 0.15)
|
|
348
|
-
SpringKeyframe(1.0, duration: 0.3)
|
|
349
|
-
}
|
|
350
|
-
KeyframeTrack(\.rotation) {
|
|
351
|
-
LinearKeyframe(.degrees(-5), duration: 0.1)
|
|
352
|
-
LinearKeyframe(.degrees(5), duration: 0.1)
|
|
353
|
-
SpringKeyframe(.zero, duration: 0.2)
|
|
354
|
-
}
|
|
355
|
-
KeyframeTrack(\.opacity) {
|
|
356
|
-
MoveKeyframe(1.0)
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
.onTapGesture { trigger.toggle() }
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
### Repeating Keyframe Animation
|
|
365
|
-
|
|
366
|
-
```swift
|
|
367
|
-
KeyframeAnimator(
|
|
368
|
-
initialValue: PulseValues(),
|
|
369
|
-
repeating: true
|
|
370
|
-
) { value in
|
|
371
|
-
Circle()
|
|
372
|
-
.fill(.blue)
|
|
373
|
-
.frame(width: 40, height: 40)
|
|
374
|
-
.scaleEffect(value.scale)
|
|
375
|
-
.opacity(value.opacity)
|
|
376
|
-
} keyframes: { _ in
|
|
377
|
-
KeyframeTrack(\.scale) {
|
|
378
|
-
CubicKeyframe(1.3, duration: 0.5)
|
|
379
|
-
CubicKeyframe(1.0, duration: 0.5)
|
|
380
|
-
}
|
|
381
|
-
KeyframeTrack(\.opacity) {
|
|
382
|
-
CubicKeyframe(0.6, duration: 0.5)
|
|
383
|
-
CubicKeyframe(1.0, duration: 0.5)
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
### Keyframe Type Reference
|
|
389
|
-
|
|
390
|
-
| Type | Interpolation | Use case |
|
|
391
|
-
|---|---|---|
|
|
392
|
-
| `LinearKeyframe(value, duration:)` | Straight line between values | Steady movement |
|
|
393
|
-
| `CubicKeyframe(value, duration:)` | Cubic bezier curve | Smooth easing |
|
|
394
|
-
| `SpringKeyframe(value, duration:, spring:)` | Spring physics | Natural settle |
|
|
395
|
-
| `MoveKeyframe(value)` | Instant jump | Reset to value immediately |
|
|
396
|
-
|
|
397
|
-
### KeyframeTimeline for Manual Evaluation
|
|
398
|
-
|
|
399
|
-
```swift
|
|
400
|
-
let timeline = KeyframeTimeline(initialValue: AnimValues()) {
|
|
401
|
-
KeyframeTrack(\.scale) {
|
|
402
|
-
CubicKeyframe(1.5, duration: 0.3)
|
|
403
|
-
CubicKeyframe(1.0, duration: 0.4)
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
let totalDuration = timeline.duration
|
|
408
|
-
let valueAtHalf = timeline.value(time: totalDuration / 2)
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
## Transaction and TransactionKey
|
|
412
|
-
|
|
413
|
-
### Transaction Basics
|
|
414
|
-
|
|
415
|
-
A `Transaction` carries the animation context for a state change. Every
|
|
416
|
-
`withAnimation` call creates a transaction internally.
|
|
417
|
-
|
|
418
|
-
```swift
|
|
419
|
-
// Explicit transaction
|
|
420
|
-
var transaction = Transaction(animation: .spring)
|
|
421
|
-
withTransaction(transaction) {
|
|
422
|
-
isExpanded = true
|
|
423
|
-
}
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
### Overriding Animations with Transaction
|
|
427
|
-
|
|
428
|
-
```swift
|
|
429
|
-
// Disable animation for a subtree
|
|
430
|
-
SomeView()
|
|
431
|
-
.transaction { transaction in
|
|
432
|
-
transaction.animation = nil
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// Override animation for a subtree when a value changes
|
|
436
|
-
SomeView()
|
|
437
|
-
.transaction(value: selectedTab) { transaction in
|
|
438
|
-
transaction.animation = .smooth(duration: 0.3)
|
|
439
|
-
}
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
### Custom TransactionKey
|
|
443
|
-
|
|
444
|
-
Store custom metadata in transactions.
|
|
445
|
-
|
|
446
|
-
```swift
|
|
447
|
-
struct IsInteractiveKey: TransactionKey {
|
|
448
|
-
static let defaultValue = false
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
extension Transaction {
|
|
452
|
-
var isInteractive: Bool {
|
|
453
|
-
get { self[IsInteractiveKey.self] }
|
|
454
|
-
set { self[IsInteractiveKey.self] = newValue }
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// Usage
|
|
459
|
-
var transaction = Transaction(animation: .interactiveSpring)
|
|
460
|
-
transaction.isInteractive = true
|
|
461
|
-
withTransaction(transaction) { dragOffset = newOffset }
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
### Scoped Transaction Override
|
|
465
|
-
|
|
466
|
-
```swift
|
|
467
|
-
// Apply transaction only within a body closure
|
|
468
|
-
ParentView()
|
|
469
|
-
.transaction { $0.animation = .spring } body: { content in
|
|
470
|
-
content.scaleEffect(scale)
|
|
471
|
-
}
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
## All Transition Types (iOS 17+)
|
|
475
|
-
|
|
476
|
-
### Built-in Transitions
|
|
477
|
-
|
|
478
|
-
| Transition | Description | Example |
|
|
479
|
-
|---|---|---|
|
|
480
|
-
| `.opacity` | Fade in/out | `.transition(.opacity)` |
|
|
481
|
-
| `.slide` | Slide from leading, exit trailing | `.transition(.slide)` |
|
|
482
|
-
| `.scale` | Scale from zero | `.transition(.scale)` |
|
|
483
|
-
| `.scale(_:anchor:)` | Scale with amount and anchor | `.transition(.scale(0.5, anchor: .bottom))` |
|
|
484
|
-
| `.move(edge:)` | Move from specified edge | `.transition(.move(edge: .top))` |
|
|
485
|
-
| `.push(from:)` | Push from edge with fade | `.transition(.push(from: .trailing))` |
|
|
486
|
-
| `.offset(_:)` | Offset by CGSize | `.transition(.offset(CGSize(width: 0, height: 50)))` |
|
|
487
|
-
| `.offset(x:y:)` | Offset by x and y | `.transition(.offset(x: 0, y: -100))` |
|
|
488
|
-
| `.identity` | No visual change | `.transition(.identity)` |
|
|
489
|
-
| `.blurReplace` | Blur and scale combined | `.transition(.blurReplace)` |
|
|
490
|
-
| `.blurReplace(_:)` | Configurable blur replace | `.transition(.blurReplace(.downUp))` |
|
|
491
|
-
| `.symbolEffect` | Default symbol effect | `.transition(.symbolEffect)` |
|
|
492
|
-
| `.symbolEffect(_:options:)` | Custom symbol effect | `.transition(.symbolEffect(.appear))` |
|
|
493
|
-
|
|
494
|
-
### Combining Transitions
|
|
495
|
-
|
|
496
|
-
```swift
|
|
497
|
-
// Slide + fade
|
|
498
|
-
.transition(.slide.combined(with: .opacity))
|
|
499
|
-
|
|
500
|
-
// Move from top + scale
|
|
501
|
-
.transition(.move(edge: .top).combined(with: .scale))
|
|
502
|
-
```
|
|
503
|
-
|
|
504
|
-
### Asymmetric Transitions
|
|
505
|
-
|
|
506
|
-
Different animation for insertion vs removal.
|
|
507
|
-
|
|
508
|
-
```swift
|
|
509
|
-
.transition(.asymmetric(
|
|
510
|
-
insertion: .push(from: .bottom).combined(with: .opacity),
|
|
511
|
-
removal: .scale.combined(with: .opacity)
|
|
512
|
-
))
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
### Custom Transition
|
|
516
|
-
|
|
517
|
-
```swift
|
|
518
|
-
struct RotateTransition: Transition {
|
|
519
|
-
func body(content: Content, phase: TransitionPhase) -> some View {
|
|
520
|
-
content
|
|
521
|
-
.rotationEffect(phase.isIdentity ? .zero : .degrees(90))
|
|
522
|
-
.opacity(phase.isIdentity ? 1 : 0)
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
extension AnyTransition {
|
|
527
|
-
static var rotate: AnyTransition {
|
|
528
|
-
.init(RotateTransition())
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
```
|
|
532
|
-
|
|
533
|
-
### TransitionPhase
|
|
534
|
-
|
|
535
|
-
```swift
|
|
536
|
-
enum TransitionPhase {
|
|
537
|
-
case willAppear // View is about to be inserted
|
|
538
|
-
case identity // View is fully presented
|
|
539
|
-
case didDisappear // View is being removed
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
// Check current phase
|
|
543
|
-
phase.isIdentity // true when fully presented
|
|
544
|
-
```
|
|
545
|
-
|
|
546
|
-
### Attaching Animation to Transition
|
|
547
|
-
|
|
548
|
-
```swift
|
|
549
|
-
.transition(
|
|
550
|
-
.move(edge: .bottom)
|
|
551
|
-
.combined(with: .opacity)
|
|
552
|
-
.animation(.spring(duration: 0.4, bounce: 0.2))
|
|
553
|
-
)
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
## All Symbol Effect Types
|
|
557
|
-
|
|
558
|
-
### Discrete Effects (trigger with `value:`)
|
|
559
|
-
|
|
560
|
-
| Effect | Scope | Direction |
|
|
561
|
-
|---|---|---|
|
|
562
|
-
| `.bounce` | `.byLayer`, `.wholeSymbol` | -- |
|
|
563
|
-
| `.wiggle` | `.byLayer`, `.wholeSymbol` | `.up`, `.down`, `.left`, `.right`, `.forward`, `.backward`, `.clockwise`, `.counterClockwise`, `.custom(angle:)` |
|
|
564
|
-
|
|
565
|
-
```swift
|
|
566
|
-
Image(systemName: "bell.fill")
|
|
567
|
-
.symbolEffect(.bounce.byLayer, value: count)
|
|
568
|
-
|
|
569
|
-
Image(systemName: "arrow.left.arrow.right")
|
|
570
|
-
.symbolEffect(.wiggle.left, value: swapCount)
|
|
571
|
-
```
|
|
572
|
-
|
|
573
|
-
### Indefinite Effects (toggle with `isActive:`)
|
|
574
|
-
|
|
575
|
-
| Effect | Scope | Direction |
|
|
576
|
-
|---|---|---|
|
|
577
|
-
| `.pulse` | `.byLayer`, `.wholeSymbol` | -- |
|
|
578
|
-
| `.variableColor` | `.byLayer`, `.wholeSymbol` | Chaining: `.cumulative`/`.iterative`, `.reversing`/`.nonReversing`, `.dimInactiveLayers`/`.hideInactiveLayers` |
|
|
579
|
-
| `.scale` | `.byLayer`, `.wholeSymbol` | `.up`, `.down` |
|
|
580
|
-
| `.breathe` | `.byLayer`, `.wholeSymbol` | -- |
|
|
581
|
-
| `.rotate` | `.byLayer`, `.wholeSymbol` | `.clockwise`, `.counterClockwise` |
|
|
582
|
-
|
|
583
|
-
```swift
|
|
584
|
-
Image(systemName: "wifi")
|
|
585
|
-
.symbolEffect(.pulse.byLayer, isActive: isConnecting)
|
|
586
|
-
|
|
587
|
-
Image(systemName: "gear")
|
|
588
|
-
.symbolEffect(.rotate.clockwise, isActive: isProcessing)
|
|
589
|
-
|
|
590
|
-
Image(systemName: "speaker.wave.3.fill")
|
|
591
|
-
.symbolEffect(
|
|
592
|
-
.variableColor.cumulative.nonReversing.dimInactiveLayers,
|
|
593
|
-
options: .repeating,
|
|
594
|
-
isActive: isPlaying
|
|
595
|
-
)
|
|
596
|
-
|
|
597
|
-
Image(systemName: "magnifyingglass")
|
|
598
|
-
.symbolEffect(.scale.up, isActive: isHighlighted)
|
|
599
|
-
|
|
600
|
-
Image(systemName: "heart.fill")
|
|
601
|
-
.symbolEffect(.breathe, isActive: isFavorite)
|
|
602
|
-
```
|
|
603
|
-
|
|
604
|
-
### Transition Effects (appear/disappear)
|
|
605
|
-
|
|
606
|
-
```swift
|
|
607
|
-
Image(systemName: "checkmark.circle.fill")
|
|
608
|
-
.symbolEffect(.appear, isActive: showCheck)
|
|
609
|
-
|
|
610
|
-
Image(systemName: "xmark.circle")
|
|
611
|
-
.symbolEffect(.disappear, isActive: shouldHide)
|
|
612
|
-
```
|
|
613
|
-
|
|
614
|
-
### Content Transition Effects (replace)
|
|
615
|
-
|
|
616
|
-
```swift
|
|
617
|
-
Image(systemName: isMuted ? "speaker.slash" : "speaker.wave.3")
|
|
618
|
-
.contentTransition(.symbolEffect(.replace.downUp))
|
|
619
|
-
|
|
620
|
-
// Magic replace (morphs between symbols)
|
|
621
|
-
Image(systemName: isPlaying ? "pause.fill" : "play.fill")
|
|
622
|
-
.contentTransition(.symbolEffect(.replace.magic(fallback: .downUp)))
|
|
623
|
-
```
|
|
624
|
-
|
|
625
|
-
Replace directions: `.downUp`, `.offUp`, `.upUp`.
|
|
626
|
-
|
|
627
|
-
### SymbolEffectOptions
|
|
628
|
-
|
|
629
|
-
```swift
|
|
630
|
-
.symbolEffect(.pulse, options: .default, isActive: true)
|
|
631
|
-
.symbolEffect(.bounce, options: .repeating, value: count)
|
|
632
|
-
.symbolEffect(.pulse, options: .nonRepeating, isActive: true)
|
|
633
|
-
.symbolEffect(.bounce, options: .repeat(3), value: count)
|
|
634
|
-
.symbolEffect(.pulse, options: .speed(2.0), isActive: true)
|
|
635
|
-
|
|
636
|
-
// RepeatBehavior
|
|
637
|
-
.symbolEffect(.bounce, options: .repeat(.periodic(3, delay: 0.5)), value: count)
|
|
638
|
-
.symbolEffect(.pulse, options: .repeat(.continuous), isActive: true)
|
|
639
|
-
```
|
|
640
|
-
|
|
641
|
-
### Removing Effects
|
|
642
|
-
|
|
643
|
-
```swift
|
|
644
|
-
Image(systemName: "star.fill")
|
|
645
|
-
.symbolEffect(.pulse, isActive: true)
|
|
646
|
-
.symbolEffectsRemoved(reduceMotion)
|
|
647
|
-
```
|
|
648
|
-
|
|
649
|
-
## Reduce Motion Implementation Patterns
|
|
650
|
-
|
|
651
|
-
### Environment Variable
|
|
652
|
-
|
|
653
|
-
```swift
|
|
654
|
-
@Environment(\.accessibilityReduceMotion) private var reduceMotion
|
|
655
|
-
```
|
|
656
|
-
|
|
657
|
-
### Pattern 1: Conditional Animation
|
|
658
|
-
|
|
659
|
-
```swift
|
|
660
|
-
withAnimation(reduceMotion ? .none : .bouncy) {
|
|
661
|
-
isExpanded.toggle()
|
|
662
|
-
}
|
|
663
|
-
```
|
|
664
|
-
|
|
665
|
-
### Pattern 2: Simplified Animation
|
|
666
|
-
|
|
667
|
-
Replace bouncy/spring with crossfade when reduce motion is on.
|
|
668
|
-
|
|
669
|
-
```swift
|
|
670
|
-
withAnimation(reduceMotion ? .easeInOut(duration: 0.2) : .spring(duration: 0.4, bounce: 0.3)) {
|
|
671
|
-
selectedTab = newTab
|
|
672
|
-
}
|
|
673
|
-
```
|
|
674
|
-
|
|
675
|
-
### Pattern 3: Disable Repeating Animations
|
|
676
|
-
|
|
677
|
-
```swift
|
|
678
|
-
// WRONG: Ignores reduce motion
|
|
679
|
-
PhaseAnimator(phases) { phase in /* ... */ }
|
|
680
|
-
|
|
681
|
-
// CORRECT: Use trigger-based or skip entirely
|
|
682
|
-
if !reduceMotion {
|
|
683
|
-
PhaseAnimator(phases) { phase in /* ... */ }
|
|
684
|
-
} else {
|
|
685
|
-
StaticView()
|
|
686
|
-
}
|
|
687
|
-
```
|
|
688
|
-
|
|
689
|
-
### Pattern 4: Symbol Effects
|
|
690
|
-
|
|
691
|
-
```swift
|
|
692
|
-
Image(systemName: "wifi")
|
|
693
|
-
.symbolEffect(.pulse, isActive: isSearching)
|
|
694
|
-
.symbolEffectsRemoved(reduceMotion)
|
|
695
|
-
```
|
|
696
|
-
|
|
697
|
-
### Pattern 5: Reusable Helper
|
|
698
|
-
|
|
699
|
-
```swift
|
|
700
|
-
extension Animation {
|
|
701
|
-
static func adaptive(
|
|
702
|
-
_ animation: Animation,
|
|
703
|
-
reduceMotion: Bool
|
|
704
|
-
) -> Animation? {
|
|
705
|
-
reduceMotion ? nil : animation
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
// Usage
|
|
710
|
-
withAnimation(.adaptive(.bouncy, reduceMotion: reduceMotion)) {
|
|
711
|
-
isVisible = true
|
|
712
|
-
}
|
|
713
|
-
```
|
|
714
|
-
|
|
715
|
-
## Animation Performance Tips
|
|
716
|
-
|
|
717
|
-
### Keep Content Closures Light
|
|
718
|
-
|
|
719
|
-
The `content` closure in `KeyframeAnimator` and `PhaseAnimator` runs every
|
|
720
|
-
frame while animating. Keep it to simple view modifiers.
|
|
721
|
-
|
|
722
|
-
```swift
|
|
723
|
-
// WRONG: Expensive computation per frame
|
|
724
|
-
.keyframeAnimator(initialValue: v, trigger: t) { content, value in
|
|
725
|
-
let result = heavyComputation(value.progress)
|
|
726
|
-
return content.opacity(result)
|
|
727
|
-
} keyframes: { _ in /* ... */ }
|
|
728
|
-
|
|
729
|
-
// CORRECT: Only apply view modifiers
|
|
730
|
-
.keyframeAnimator(initialValue: v, trigger: t) { content, value in
|
|
731
|
-
content.opacity(value.opacity)
|
|
732
|
-
} keyframes: { _ in /* ... */ }
|
|
733
|
-
```
|
|
734
|
-
|
|
735
|
-
### Prefer Modifier-Based Animations
|
|
736
|
-
|
|
737
|
-
Animating view modifiers (`opacity`, `scaleEffect`, `offset`, `rotationEffect`)
|
|
738
|
-
is highly optimized. Avoid animating layout-triggering properties when possible.
|
|
739
|
-
|
|
740
|
-
### Use drawingGroup for Complex Compositing
|
|
741
|
-
|
|
742
|
-
```swift
|
|
743
|
-
ComplexAnimatedView()
|
|
744
|
-
.drawingGroup()
|
|
745
|
-
```
|
|
746
|
-
|
|
747
|
-
Flattens the view hierarchy into a single Metal-backed layer. Use when
|
|
748
|
-
compositing many overlapping animated views.
|
|
749
|
-
|
|
750
|
-
### Limit Concurrent Animations
|
|
751
|
-
|
|
752
|
-
Avoid animating dozens of views simultaneously. Use staggered delays.
|
|
753
|
-
|
|
754
|
-
```swift
|
|
755
|
-
ForEach(Array(items.enumerated()), id: \.element.id) { index, item in
|
|
756
|
-
ItemView(item: item)
|
|
757
|
-
.transition(.move(edge: .bottom).combined(with: .opacity))
|
|
758
|
-
.animation(.spring.delay(Double(index) * 0.05), value: isVisible)
|
|
759
|
-
}
|
|
760
|
-
```
|
|
761
|
-
|
|
762
|
-
### Avoid Re-creating Views During Animation
|
|
763
|
-
|
|
764
|
-
Ensure animated views maintain stable identity. Use explicit `id()` modifiers
|
|
765
|
-
or stable `ForEach` identifiers.
|
|
766
|
-
|
|
767
|
-
```swift
|
|
768
|
-
// WRONG: View identity changes, breaks animation
|
|
769
|
-
ForEach(Array(items.enumerated()), id: \.offset) { index, item in
|
|
770
|
-
ItemView(item: item)
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
// CORRECT: Stable identity from model
|
|
774
|
-
ForEach(items) { item in
|
|
775
|
-
ItemView(item: item)
|
|
776
|
-
}
|
|
777
|
-
```
|
|
778
|
-
|
|
779
|
-
### Use geometryGroup() for Nested Geometry
|
|
780
|
-
|
|
781
|
-
Isolate child geometry from parent animations when they conflict.
|
|
782
|
-
|
|
783
|
-
```swift
|
|
784
|
-
ParentView()
|
|
785
|
-
.scaleEffect(parentScale)
|
|
786
|
-
.geometryGroup() // children see stable geometry
|
|
787
|
-
```
|
|
788
|
-
|
|
789
|
-
### Transaction for Selective Animation Override
|
|
790
|
-
|
|
791
|
-
Override animation for specific subtrees without affecting siblings.
|
|
792
|
-
|
|
793
|
-
```swift
|
|
794
|
-
// Disable animation on one child while parent animates
|
|
795
|
-
ChildView()
|
|
796
|
-
.transaction { $0.animation = nil }
|
|
797
|
-
```
|
|
798
|
-
|
|
799
|
-
### Profile with Instruments
|
|
800
|
-
|
|
801
|
-
Use the Core Animation instrument in Xcode Instruments to verify:
|
|
802
|
-
- Frame rate stays at 120 fps (ProMotion devices) or 60 fps.
|
|
803
|
-
- No offscreen rendering passes.
|
|
804
|
-
- GPU utilization stays reasonable during animations.
|
|
1
|
+
# SwiftUI Animation Advanced Reference
|
|
2
|
+
|
|
3
|
+
Detailed API reference for SwiftUI animation types, protocols, and patterns.
|
|
4
|
+
Covers material beyond the SKILL.md summary.
|
|
5
|
+
|
|
6
|
+
## Contents
|
|
7
|
+
|
|
8
|
+
- [CustomAnimation Protocol (iOS 17+)](#customanimation-protocol-ios-17)
|
|
9
|
+
- [Spring Type -- All Initializer Variants](#spring-type-all-initializer-variants)
|
|
10
|
+
- [UnitCurve Types (iOS 17+)](#unitcurve-types-ios-17)
|
|
11
|
+
- [PhaseAnimator Deep Patterns](#phaseanimator-deep-patterns)
|
|
12
|
+
- [KeyframeAnimator Multi-Track Examples](#keyframeanimator-multi-track-examples)
|
|
13
|
+
- [Transaction and TransactionKey](#transaction-and-transactionkey)
|
|
14
|
+
- [All Transition Types (iOS 17+)](#all-transition-types-ios-17)
|
|
15
|
+
- [All Symbol Effect Types](#all-symbol-effect-types)
|
|
16
|
+
- [Reduce Motion Implementation Patterns](#reduce-motion-implementation-patterns)
|
|
17
|
+
- [Animation Performance Tips](#animation-performance-tips)
|
|
18
|
+
|
|
19
|
+
## CustomAnimation Protocol (iOS 17+)
|
|
20
|
+
|
|
21
|
+
Create entirely custom animation curves by conforming to `CustomAnimation`.
|
|
22
|
+
|
|
23
|
+
```swift
|
|
24
|
+
@preconcurrency protocol CustomAnimation: Hashable, Sendable
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Required Method
|
|
28
|
+
|
|
29
|
+
```swift
|
|
30
|
+
func animate<V: VectorArithmetic>(
|
|
31
|
+
value: V,
|
|
32
|
+
time: TimeInterval,
|
|
33
|
+
context: inout AnimationContext<V>
|
|
34
|
+
) -> V?
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Return the interpolated value at the given time. Return `nil` when the
|
|
38
|
+
animation is complete.
|
|
39
|
+
|
|
40
|
+
### Optional Methods
|
|
41
|
+
|
|
42
|
+
```swift
|
|
43
|
+
func velocity<V: VectorArithmetic>(
|
|
44
|
+
value: V,
|
|
45
|
+
time: TimeInterval,
|
|
46
|
+
context: AnimationContext<V>
|
|
47
|
+
) -> V?
|
|
48
|
+
|
|
49
|
+
func shouldMerge<V: VectorArithmetic>(
|
|
50
|
+
previous: Animation,
|
|
51
|
+
value: V,
|
|
52
|
+
time: TimeInterval,
|
|
53
|
+
context: inout AnimationContext<V>
|
|
54
|
+
) -> Bool
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Full Example: Elastic Ease-In-Out
|
|
58
|
+
|
|
59
|
+
```swift
|
|
60
|
+
struct ElasticAnimation: CustomAnimation {
|
|
61
|
+
let duration: TimeInterval
|
|
62
|
+
|
|
63
|
+
func animate<V: VectorArithmetic>(
|
|
64
|
+
value: V,
|
|
65
|
+
time: TimeInterval,
|
|
66
|
+
context: inout AnimationContext<V>
|
|
67
|
+
) -> V? {
|
|
68
|
+
guard time <= duration else { return nil }
|
|
69
|
+
let p = time / duration
|
|
70
|
+
let s = sin((20 * p - 11.125) * ((2 * .pi) / 4.5))
|
|
71
|
+
let progress: Double
|
|
72
|
+
if p < 0.5 {
|
|
73
|
+
progress = -(pow(2, 20 * p - 10) * s) / 2
|
|
74
|
+
} else {
|
|
75
|
+
progress = (pow(2, -20 * p + 10) * s) / 2 + 1
|
|
76
|
+
}
|
|
77
|
+
return value.scaled(by: progress)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Ergonomic Extension Pattern
|
|
83
|
+
|
|
84
|
+
Expose custom animations as static members on `Animation`.
|
|
85
|
+
|
|
86
|
+
```swift
|
|
87
|
+
extension Animation {
|
|
88
|
+
static var elastic: Animation {
|
|
89
|
+
elastic(duration: 0.35)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
static func elastic(duration: TimeInterval) -> Animation {
|
|
93
|
+
Animation(ElasticAnimation(duration: duration))
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Usage
|
|
98
|
+
withAnimation(.elastic(duration: 0.5)) { isActive.toggle() }
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Supporting Types
|
|
102
|
+
|
|
103
|
+
| Type | Role |
|
|
104
|
+
|---|---|
|
|
105
|
+
| `AnimationContext<V>` | Carries environment and per-animation state |
|
|
106
|
+
| `AnimationState` | Key-value storage for persisted state |
|
|
107
|
+
| `AnimationStateKey` | Protocol for defining custom state keys |
|
|
108
|
+
|
|
109
|
+
## Spring Type -- All Initializer Variants
|
|
110
|
+
|
|
111
|
+
### Perceptual (Preferred)
|
|
112
|
+
|
|
113
|
+
```swift
|
|
114
|
+
Spring(duration: 0.5, bounce: 0.0)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- `duration` -- Perceptual duration controlling pace. Default `0.5`.
|
|
118
|
+
- `bounce` -- Bounciness. `0.0` = no bounce, `1.0` = undamped. Negative values
|
|
119
|
+
produce overdamped springs. Default `0.0`.
|
|
120
|
+
|
|
121
|
+
### Physical Parameters
|
|
122
|
+
|
|
123
|
+
```swift
|
|
124
|
+
Spring(mass: 1.0, stiffness: 100.0, damping: 10.0, allowOverDamping: false)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
- `mass` -- Mass at end of spring. Default `1.0`.
|
|
128
|
+
- `stiffness` -- Spring stiffness coefficient.
|
|
129
|
+
- `damping` -- Friction-like drag force.
|
|
130
|
+
- `allowOverDamping` -- Permit damping ratio > 1. Default `false`.
|
|
131
|
+
|
|
132
|
+
### Response-Based
|
|
133
|
+
|
|
134
|
+
```swift
|
|
135
|
+
Spring(response: 0.5, dampingRatio: 0.7)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
- `response` -- Stiffness expressed as approximate duration in seconds.
|
|
139
|
+
- `dampingRatio` -- Fraction of critical damping. `1.0` = critically damped.
|
|
140
|
+
|
|
141
|
+
### Settling-Based
|
|
142
|
+
|
|
143
|
+
```swift
|
|
144
|
+
Spring(settlingDuration: 1.0, dampingRatio: 0.8, epsilon: 0.001)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
- `settlingDuration` -- Estimated time to come to rest.
|
|
148
|
+
- `dampingRatio` -- Fraction of critical damping.
|
|
149
|
+
- `epsilon` -- Threshold for considering the spring at rest. Default `0.001`.
|
|
150
|
+
|
|
151
|
+
### Presets
|
|
152
|
+
|
|
153
|
+
```swift
|
|
154
|
+
Spring.smooth // no bounce
|
|
155
|
+
Spring.smooth(duration: 0.5, extraBounce: 0.0)
|
|
156
|
+
Spring.snappy // small bounce
|
|
157
|
+
Spring.snappy(duration: 0.4, extraBounce: 0.1)
|
|
158
|
+
Spring.bouncy // visible bounce
|
|
159
|
+
Spring.bouncy(duration: 0.5, extraBounce: 0.2)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Querying State
|
|
163
|
+
|
|
164
|
+
```swift
|
|
165
|
+
let spring = Spring(duration: 0.5, bounce: 0.3)
|
|
166
|
+
let v = spring.value(target: 1.0, initialVelocity: 0.0, time: 0.25)
|
|
167
|
+
let vel = spring.velocity(target: 1.0, initialVelocity: 0.0, time: 0.25)
|
|
168
|
+
let settle = spring.settlingDuration(target: 1.0, initialVelocity: 0.0, epsilon: 0.001)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Parameter Conversion
|
|
172
|
+
|
|
173
|
+
```swift
|
|
174
|
+
let spring = Spring(duration: 0.5, bounce: 0.3)
|
|
175
|
+
// Access physical equivalents:
|
|
176
|
+
spring.mass // 1.0
|
|
177
|
+
spring.stiffness // 157.9
|
|
178
|
+
spring.damping // 17.6
|
|
179
|
+
spring.response
|
|
180
|
+
spring.dampingRatio
|
|
181
|
+
spring.settlingDuration
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## UnitCurve Types (iOS 17+)
|
|
185
|
+
|
|
186
|
+
Map input progress [0,1] to output progress [0,1]. Used with
|
|
187
|
+
`.timingCurve(_:duration:)`.
|
|
188
|
+
|
|
189
|
+
### Built-in Curves
|
|
190
|
+
|
|
191
|
+
```swift
|
|
192
|
+
UnitCurve.linear
|
|
193
|
+
UnitCurve.easeIn
|
|
194
|
+
UnitCurve.easeOut
|
|
195
|
+
UnitCurve.easeInOut
|
|
196
|
+
UnitCurve.circularEaseIn
|
|
197
|
+
UnitCurve.circularEaseOut
|
|
198
|
+
UnitCurve.circularEaseInOut
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Custom Bezier Curve
|
|
202
|
+
|
|
203
|
+
```swift
|
|
204
|
+
UnitCurve.bezier(
|
|
205
|
+
startControlPoint: UnitPoint(x: 0.42, y: 0.0),
|
|
206
|
+
endControlPoint: UnitPoint(x: 0.58, y: 1.0)
|
|
207
|
+
)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Instance Members
|
|
211
|
+
|
|
212
|
+
```swift
|
|
213
|
+
let curve = UnitCurve.easeInOut
|
|
214
|
+
curve.value(at: 0.5) // output progress at midpoint
|
|
215
|
+
curve.velocity(at: 0.5) // rate of change at midpoint
|
|
216
|
+
curve.inverse // swaps x and y components
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Usage with Animation
|
|
220
|
+
|
|
221
|
+
```swift
|
|
222
|
+
.animation(.timingCurve(UnitCurve.circularEaseIn, duration: 0.4), value: x)
|
|
223
|
+
|
|
224
|
+
// Cubic bezier control points
|
|
225
|
+
.animation(.timingCurve(0.68, -0.55, 0.27, 1.55, duration: 0.5), value: x)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## PhaseAnimator Deep Patterns
|
|
229
|
+
|
|
230
|
+
### Multi-Phase with Complex State
|
|
231
|
+
|
|
232
|
+
```swift
|
|
233
|
+
enum LoadPhase: CaseIterable {
|
|
234
|
+
case ready, loading, spinning, complete
|
|
235
|
+
|
|
236
|
+
var scale: Double {
|
|
237
|
+
switch self {
|
|
238
|
+
case .ready: 1.0
|
|
239
|
+
case .loading: 0.9
|
|
240
|
+
case .spinning: 1.0
|
|
241
|
+
case .complete: 1.1
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
var rotation: Angle {
|
|
246
|
+
switch self {
|
|
247
|
+
case .spinning: .degrees(360)
|
|
248
|
+
default: .zero
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
var opacity: Double {
|
|
253
|
+
self == .loading ? 0.7 : 1.0
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
struct LoadingIndicator: View {
|
|
258
|
+
var body: some View {
|
|
259
|
+
PhaseAnimator(LoadPhase.allCases) { phase in
|
|
260
|
+
Image(systemName: "arrow.triangle.2.circlepath")
|
|
261
|
+
.font(.title)
|
|
262
|
+
.scaleEffect(phase.scale)
|
|
263
|
+
.rotationEffect(phase.rotation)
|
|
264
|
+
.opacity(phase.opacity)
|
|
265
|
+
} animation: { phase in
|
|
266
|
+
switch phase {
|
|
267
|
+
case .ready: .smooth(duration: 0.2)
|
|
268
|
+
case .loading: .easeIn(duration: 0.15)
|
|
269
|
+
case .spinning: .linear(duration: 0.6)
|
|
270
|
+
case .complete: .spring(duration: 0.3, bounce: 0.4)
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Trigger-Based One-Shot
|
|
278
|
+
|
|
279
|
+
Run through all phases once each time the trigger value changes.
|
|
280
|
+
|
|
281
|
+
```swift
|
|
282
|
+
struct FeedbackDot: View {
|
|
283
|
+
@State private var feedbackTrigger = 0
|
|
284
|
+
|
|
285
|
+
var body: some View {
|
|
286
|
+
Circle()
|
|
287
|
+
.frame(width: 20, height: 20)
|
|
288
|
+
.phaseAnimator(
|
|
289
|
+
[false, true, false],
|
|
290
|
+
trigger: feedbackTrigger
|
|
291
|
+
) { content, phase in
|
|
292
|
+
content.scaleEffect(phase ? 1.5 : 1.0)
|
|
293
|
+
} animation: { _ in
|
|
294
|
+
.spring(duration: 0.25, bounce: 0.5)
|
|
295
|
+
}
|
|
296
|
+
.onTapGesture { feedbackTrigger += 1 }
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### View Modifier Form
|
|
302
|
+
|
|
303
|
+
```swift
|
|
304
|
+
Text("Hello")
|
|
305
|
+
.phaseAnimator([0.0, 1.0, 0.0]) { content, phase in
|
|
306
|
+
content.opacity(phase)
|
|
307
|
+
} animation: { _ in .easeInOut(duration: 0.8) }
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## KeyframeAnimator Multi-Track Examples
|
|
311
|
+
|
|
312
|
+
### Bounce-and-Fade
|
|
313
|
+
|
|
314
|
+
```swift
|
|
315
|
+
struct BounceValues {
|
|
316
|
+
var yOffset: Double = 0
|
|
317
|
+
var scale: Double = 1.0
|
|
318
|
+
var opacity: Double = 1.0
|
|
319
|
+
var rotation: Angle = .zero
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
struct BouncingBadge: View {
|
|
323
|
+
@State private var trigger = false
|
|
324
|
+
|
|
325
|
+
var body: some View {
|
|
326
|
+
Text("NEW")
|
|
327
|
+
.font(.caption.bold())
|
|
328
|
+
.padding(6)
|
|
329
|
+
.background(.red, in: Capsule())
|
|
330
|
+
.keyframeAnimator(
|
|
331
|
+
initialValue: BounceValues(),
|
|
332
|
+
trigger: trigger
|
|
333
|
+
) { content, value in
|
|
334
|
+
content
|
|
335
|
+
.offset(y: value.yOffset)
|
|
336
|
+
.scaleEffect(value.scale)
|
|
337
|
+
.opacity(value.opacity)
|
|
338
|
+
.rotationEffect(value.rotation)
|
|
339
|
+
} keyframes: { _ in
|
|
340
|
+
KeyframeTrack(\.yOffset) {
|
|
341
|
+
SpringKeyframe(-20, duration: 0.2)
|
|
342
|
+
CubicKeyframe(5, duration: 0.15)
|
|
343
|
+
SpringKeyframe(0, duration: 0.3)
|
|
344
|
+
}
|
|
345
|
+
KeyframeTrack(\.scale) {
|
|
346
|
+
CubicKeyframe(1.3, duration: 0.2)
|
|
347
|
+
CubicKeyframe(0.95, duration: 0.15)
|
|
348
|
+
SpringKeyframe(1.0, duration: 0.3)
|
|
349
|
+
}
|
|
350
|
+
KeyframeTrack(\.rotation) {
|
|
351
|
+
LinearKeyframe(.degrees(-5), duration: 0.1)
|
|
352
|
+
LinearKeyframe(.degrees(5), duration: 0.1)
|
|
353
|
+
SpringKeyframe(.zero, duration: 0.2)
|
|
354
|
+
}
|
|
355
|
+
KeyframeTrack(\.opacity) {
|
|
356
|
+
MoveKeyframe(1.0)
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
.onTapGesture { trigger.toggle() }
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Repeating Keyframe Animation
|
|
365
|
+
|
|
366
|
+
```swift
|
|
367
|
+
KeyframeAnimator(
|
|
368
|
+
initialValue: PulseValues(),
|
|
369
|
+
repeating: true
|
|
370
|
+
) { value in
|
|
371
|
+
Circle()
|
|
372
|
+
.fill(.blue)
|
|
373
|
+
.frame(width: 40, height: 40)
|
|
374
|
+
.scaleEffect(value.scale)
|
|
375
|
+
.opacity(value.opacity)
|
|
376
|
+
} keyframes: { _ in
|
|
377
|
+
KeyframeTrack(\.scale) {
|
|
378
|
+
CubicKeyframe(1.3, duration: 0.5)
|
|
379
|
+
CubicKeyframe(1.0, duration: 0.5)
|
|
380
|
+
}
|
|
381
|
+
KeyframeTrack(\.opacity) {
|
|
382
|
+
CubicKeyframe(0.6, duration: 0.5)
|
|
383
|
+
CubicKeyframe(1.0, duration: 0.5)
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Keyframe Type Reference
|
|
389
|
+
|
|
390
|
+
| Type | Interpolation | Use case |
|
|
391
|
+
|---|---|---|
|
|
392
|
+
| `LinearKeyframe(value, duration:)` | Straight line between values | Steady movement |
|
|
393
|
+
| `CubicKeyframe(value, duration:)` | Cubic bezier curve | Smooth easing |
|
|
394
|
+
| `SpringKeyframe(value, duration:, spring:)` | Spring physics | Natural settle |
|
|
395
|
+
| `MoveKeyframe(value)` | Instant jump | Reset to value immediately |
|
|
396
|
+
|
|
397
|
+
### KeyframeTimeline for Manual Evaluation
|
|
398
|
+
|
|
399
|
+
```swift
|
|
400
|
+
let timeline = KeyframeTimeline(initialValue: AnimValues()) {
|
|
401
|
+
KeyframeTrack(\.scale) {
|
|
402
|
+
CubicKeyframe(1.5, duration: 0.3)
|
|
403
|
+
CubicKeyframe(1.0, duration: 0.4)
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
let totalDuration = timeline.duration
|
|
408
|
+
let valueAtHalf = timeline.value(time: totalDuration / 2)
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## Transaction and TransactionKey
|
|
412
|
+
|
|
413
|
+
### Transaction Basics
|
|
414
|
+
|
|
415
|
+
A `Transaction` carries the animation context for a state change. Every
|
|
416
|
+
`withAnimation` call creates a transaction internally.
|
|
417
|
+
|
|
418
|
+
```swift
|
|
419
|
+
// Explicit transaction
|
|
420
|
+
var transaction = Transaction(animation: .spring)
|
|
421
|
+
withTransaction(transaction) {
|
|
422
|
+
isExpanded = true
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Overriding Animations with Transaction
|
|
427
|
+
|
|
428
|
+
```swift
|
|
429
|
+
// Disable animation for a subtree
|
|
430
|
+
SomeView()
|
|
431
|
+
.transaction { transaction in
|
|
432
|
+
transaction.animation = nil
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Override animation for a subtree when a value changes
|
|
436
|
+
SomeView()
|
|
437
|
+
.transaction(value: selectedTab) { transaction in
|
|
438
|
+
transaction.animation = .smooth(duration: 0.3)
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Custom TransactionKey
|
|
443
|
+
|
|
444
|
+
Store custom metadata in transactions.
|
|
445
|
+
|
|
446
|
+
```swift
|
|
447
|
+
struct IsInteractiveKey: TransactionKey {
|
|
448
|
+
static let defaultValue = false
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
extension Transaction {
|
|
452
|
+
var isInteractive: Bool {
|
|
453
|
+
get { self[IsInteractiveKey.self] }
|
|
454
|
+
set { self[IsInteractiveKey.self] = newValue }
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Usage
|
|
459
|
+
var transaction = Transaction(animation: .interactiveSpring)
|
|
460
|
+
transaction.isInteractive = true
|
|
461
|
+
withTransaction(transaction) { dragOffset = newOffset }
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Scoped Transaction Override
|
|
465
|
+
|
|
466
|
+
```swift
|
|
467
|
+
// Apply transaction only within a body closure
|
|
468
|
+
ParentView()
|
|
469
|
+
.transaction { $0.animation = .spring } body: { content in
|
|
470
|
+
content.scaleEffect(scale)
|
|
471
|
+
}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
## All Transition Types (iOS 17+)
|
|
475
|
+
|
|
476
|
+
### Built-in Transitions
|
|
477
|
+
|
|
478
|
+
| Transition | Description | Example |
|
|
479
|
+
|---|---|---|
|
|
480
|
+
| `.opacity` | Fade in/out | `.transition(.opacity)` |
|
|
481
|
+
| `.slide` | Slide from leading, exit trailing | `.transition(.slide)` |
|
|
482
|
+
| `.scale` | Scale from zero | `.transition(.scale)` |
|
|
483
|
+
| `.scale(_:anchor:)` | Scale with amount and anchor | `.transition(.scale(0.5, anchor: .bottom))` |
|
|
484
|
+
| `.move(edge:)` | Move from specified edge | `.transition(.move(edge: .top))` |
|
|
485
|
+
| `.push(from:)` | Push from edge with fade | `.transition(.push(from: .trailing))` |
|
|
486
|
+
| `.offset(_:)` | Offset by CGSize | `.transition(.offset(CGSize(width: 0, height: 50)))` |
|
|
487
|
+
| `.offset(x:y:)` | Offset by x and y | `.transition(.offset(x: 0, y: -100))` |
|
|
488
|
+
| `.identity` | No visual change | `.transition(.identity)` |
|
|
489
|
+
| `.blurReplace` | Blur and scale combined | `.transition(.blurReplace)` |
|
|
490
|
+
| `.blurReplace(_:)` | Configurable blur replace | `.transition(.blurReplace(.downUp))` |
|
|
491
|
+
| `.symbolEffect` | Default symbol effect | `.transition(.symbolEffect)` |
|
|
492
|
+
| `.symbolEffect(_:options:)` | Custom symbol effect | `.transition(.symbolEffect(.appear))` |
|
|
493
|
+
|
|
494
|
+
### Combining Transitions
|
|
495
|
+
|
|
496
|
+
```swift
|
|
497
|
+
// Slide + fade
|
|
498
|
+
.transition(.slide.combined(with: .opacity))
|
|
499
|
+
|
|
500
|
+
// Move from top + scale
|
|
501
|
+
.transition(.move(edge: .top).combined(with: .scale))
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### Asymmetric Transitions
|
|
505
|
+
|
|
506
|
+
Different animation for insertion vs removal.
|
|
507
|
+
|
|
508
|
+
```swift
|
|
509
|
+
.transition(.asymmetric(
|
|
510
|
+
insertion: .push(from: .bottom).combined(with: .opacity),
|
|
511
|
+
removal: .scale.combined(with: .opacity)
|
|
512
|
+
))
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### Custom Transition
|
|
516
|
+
|
|
517
|
+
```swift
|
|
518
|
+
struct RotateTransition: Transition {
|
|
519
|
+
func body(content: Content, phase: TransitionPhase) -> some View {
|
|
520
|
+
content
|
|
521
|
+
.rotationEffect(phase.isIdentity ? .zero : .degrees(90))
|
|
522
|
+
.opacity(phase.isIdentity ? 1 : 0)
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
extension AnyTransition {
|
|
527
|
+
static var rotate: AnyTransition {
|
|
528
|
+
.init(RotateTransition())
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
### TransitionPhase
|
|
534
|
+
|
|
535
|
+
```swift
|
|
536
|
+
enum TransitionPhase {
|
|
537
|
+
case willAppear // View is about to be inserted
|
|
538
|
+
case identity // View is fully presented
|
|
539
|
+
case didDisappear // View is being removed
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Check current phase
|
|
543
|
+
phase.isIdentity // true when fully presented
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### Attaching Animation to Transition
|
|
547
|
+
|
|
548
|
+
```swift
|
|
549
|
+
.transition(
|
|
550
|
+
.move(edge: .bottom)
|
|
551
|
+
.combined(with: .opacity)
|
|
552
|
+
.animation(.spring(duration: 0.4, bounce: 0.2))
|
|
553
|
+
)
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
## All Symbol Effect Types
|
|
557
|
+
|
|
558
|
+
### Discrete Effects (trigger with `value:`)
|
|
559
|
+
|
|
560
|
+
| Effect | Scope | Direction |
|
|
561
|
+
|---|---|---|
|
|
562
|
+
| `.bounce` | `.byLayer`, `.wholeSymbol` | -- |
|
|
563
|
+
| `.wiggle` | `.byLayer`, `.wholeSymbol` | `.up`, `.down`, `.left`, `.right`, `.forward`, `.backward`, `.clockwise`, `.counterClockwise`, `.custom(angle:)` |
|
|
564
|
+
|
|
565
|
+
```swift
|
|
566
|
+
Image(systemName: "bell.fill")
|
|
567
|
+
.symbolEffect(.bounce.byLayer, value: count)
|
|
568
|
+
|
|
569
|
+
Image(systemName: "arrow.left.arrow.right")
|
|
570
|
+
.symbolEffect(.wiggle.left, value: swapCount)
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
### Indefinite Effects (toggle with `isActive:`)
|
|
574
|
+
|
|
575
|
+
| Effect | Scope | Direction |
|
|
576
|
+
|---|---|---|
|
|
577
|
+
| `.pulse` | `.byLayer`, `.wholeSymbol` | -- |
|
|
578
|
+
| `.variableColor` | `.byLayer`, `.wholeSymbol` | Chaining: `.cumulative`/`.iterative`, `.reversing`/`.nonReversing`, `.dimInactiveLayers`/`.hideInactiveLayers` |
|
|
579
|
+
| `.scale` | `.byLayer`, `.wholeSymbol` | `.up`, `.down` |
|
|
580
|
+
| `.breathe` | `.byLayer`, `.wholeSymbol` | -- |
|
|
581
|
+
| `.rotate` | `.byLayer`, `.wholeSymbol` | `.clockwise`, `.counterClockwise` |
|
|
582
|
+
|
|
583
|
+
```swift
|
|
584
|
+
Image(systemName: "wifi")
|
|
585
|
+
.symbolEffect(.pulse.byLayer, isActive: isConnecting)
|
|
586
|
+
|
|
587
|
+
Image(systemName: "gear")
|
|
588
|
+
.symbolEffect(.rotate.clockwise, isActive: isProcessing)
|
|
589
|
+
|
|
590
|
+
Image(systemName: "speaker.wave.3.fill")
|
|
591
|
+
.symbolEffect(
|
|
592
|
+
.variableColor.cumulative.nonReversing.dimInactiveLayers,
|
|
593
|
+
options: .repeating,
|
|
594
|
+
isActive: isPlaying
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
Image(systemName: "magnifyingglass")
|
|
598
|
+
.symbolEffect(.scale.up, isActive: isHighlighted)
|
|
599
|
+
|
|
600
|
+
Image(systemName: "heart.fill")
|
|
601
|
+
.symbolEffect(.breathe, isActive: isFavorite)
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
### Transition Effects (appear/disappear)
|
|
605
|
+
|
|
606
|
+
```swift
|
|
607
|
+
Image(systemName: "checkmark.circle.fill")
|
|
608
|
+
.symbolEffect(.appear, isActive: showCheck)
|
|
609
|
+
|
|
610
|
+
Image(systemName: "xmark.circle")
|
|
611
|
+
.symbolEffect(.disappear, isActive: shouldHide)
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
### Content Transition Effects (replace)
|
|
615
|
+
|
|
616
|
+
```swift
|
|
617
|
+
Image(systemName: isMuted ? "speaker.slash" : "speaker.wave.3")
|
|
618
|
+
.contentTransition(.symbolEffect(.replace.downUp))
|
|
619
|
+
|
|
620
|
+
// Magic replace (morphs between symbols)
|
|
621
|
+
Image(systemName: isPlaying ? "pause.fill" : "play.fill")
|
|
622
|
+
.contentTransition(.symbolEffect(.replace.magic(fallback: .downUp)))
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
Replace directions: `.downUp`, `.offUp`, `.upUp`.
|
|
626
|
+
|
|
627
|
+
### SymbolEffectOptions
|
|
628
|
+
|
|
629
|
+
```swift
|
|
630
|
+
.symbolEffect(.pulse, options: .default, isActive: true)
|
|
631
|
+
.symbolEffect(.bounce, options: .repeating, value: count)
|
|
632
|
+
.symbolEffect(.pulse, options: .nonRepeating, isActive: true)
|
|
633
|
+
.symbolEffect(.bounce, options: .repeat(3), value: count)
|
|
634
|
+
.symbolEffect(.pulse, options: .speed(2.0), isActive: true)
|
|
635
|
+
|
|
636
|
+
// RepeatBehavior
|
|
637
|
+
.symbolEffect(.bounce, options: .repeat(.periodic(3, delay: 0.5)), value: count)
|
|
638
|
+
.symbolEffect(.pulse, options: .repeat(.continuous), isActive: true)
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
### Removing Effects
|
|
642
|
+
|
|
643
|
+
```swift
|
|
644
|
+
Image(systemName: "star.fill")
|
|
645
|
+
.symbolEffect(.pulse, isActive: true)
|
|
646
|
+
.symbolEffectsRemoved(reduceMotion)
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
## Reduce Motion Implementation Patterns
|
|
650
|
+
|
|
651
|
+
### Environment Variable
|
|
652
|
+
|
|
653
|
+
```swift
|
|
654
|
+
@Environment(\.accessibilityReduceMotion) private var reduceMotion
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
### Pattern 1: Conditional Animation
|
|
658
|
+
|
|
659
|
+
```swift
|
|
660
|
+
withAnimation(reduceMotion ? .none : .bouncy) {
|
|
661
|
+
isExpanded.toggle()
|
|
662
|
+
}
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
### Pattern 2: Simplified Animation
|
|
666
|
+
|
|
667
|
+
Replace bouncy/spring with crossfade when reduce motion is on.
|
|
668
|
+
|
|
669
|
+
```swift
|
|
670
|
+
withAnimation(reduceMotion ? .easeInOut(duration: 0.2) : .spring(duration: 0.4, bounce: 0.3)) {
|
|
671
|
+
selectedTab = newTab
|
|
672
|
+
}
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
### Pattern 3: Disable Repeating Animations
|
|
676
|
+
|
|
677
|
+
```swift
|
|
678
|
+
// WRONG: Ignores reduce motion
|
|
679
|
+
PhaseAnimator(phases) { phase in /* ... */ }
|
|
680
|
+
|
|
681
|
+
// CORRECT: Use trigger-based or skip entirely
|
|
682
|
+
if !reduceMotion {
|
|
683
|
+
PhaseAnimator(phases) { phase in /* ... */ }
|
|
684
|
+
} else {
|
|
685
|
+
StaticView()
|
|
686
|
+
}
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### Pattern 4: Symbol Effects
|
|
690
|
+
|
|
691
|
+
```swift
|
|
692
|
+
Image(systemName: "wifi")
|
|
693
|
+
.symbolEffect(.pulse, isActive: isSearching)
|
|
694
|
+
.symbolEffectsRemoved(reduceMotion)
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
### Pattern 5: Reusable Helper
|
|
698
|
+
|
|
699
|
+
```swift
|
|
700
|
+
extension Animation {
|
|
701
|
+
static func adaptive(
|
|
702
|
+
_ animation: Animation,
|
|
703
|
+
reduceMotion: Bool
|
|
704
|
+
) -> Animation? {
|
|
705
|
+
reduceMotion ? nil : animation
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Usage
|
|
710
|
+
withAnimation(.adaptive(.bouncy, reduceMotion: reduceMotion)) {
|
|
711
|
+
isVisible = true
|
|
712
|
+
}
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
## Animation Performance Tips
|
|
716
|
+
|
|
717
|
+
### Keep Content Closures Light
|
|
718
|
+
|
|
719
|
+
The `content` closure in `KeyframeAnimator` and `PhaseAnimator` runs every
|
|
720
|
+
frame while animating. Keep it to simple view modifiers.
|
|
721
|
+
|
|
722
|
+
```swift
|
|
723
|
+
// WRONG: Expensive computation per frame
|
|
724
|
+
.keyframeAnimator(initialValue: v, trigger: t) { content, value in
|
|
725
|
+
let result = heavyComputation(value.progress)
|
|
726
|
+
return content.opacity(result)
|
|
727
|
+
} keyframes: { _ in /* ... */ }
|
|
728
|
+
|
|
729
|
+
// CORRECT: Only apply view modifiers
|
|
730
|
+
.keyframeAnimator(initialValue: v, trigger: t) { content, value in
|
|
731
|
+
content.opacity(value.opacity)
|
|
732
|
+
} keyframes: { _ in /* ... */ }
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
### Prefer Modifier-Based Animations
|
|
736
|
+
|
|
737
|
+
Animating view modifiers (`opacity`, `scaleEffect`, `offset`, `rotationEffect`)
|
|
738
|
+
is highly optimized. Avoid animating layout-triggering properties when possible.
|
|
739
|
+
|
|
740
|
+
### Use drawingGroup for Complex Compositing
|
|
741
|
+
|
|
742
|
+
```swift
|
|
743
|
+
ComplexAnimatedView()
|
|
744
|
+
.drawingGroup()
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
Flattens the view hierarchy into a single Metal-backed layer. Use when
|
|
748
|
+
compositing many overlapping animated views.
|
|
749
|
+
|
|
750
|
+
### Limit Concurrent Animations
|
|
751
|
+
|
|
752
|
+
Avoid animating dozens of views simultaneously. Use staggered delays.
|
|
753
|
+
|
|
754
|
+
```swift
|
|
755
|
+
ForEach(Array(items.enumerated()), id: \.element.id) { index, item in
|
|
756
|
+
ItemView(item: item)
|
|
757
|
+
.transition(.move(edge: .bottom).combined(with: .opacity))
|
|
758
|
+
.animation(.spring.delay(Double(index) * 0.05), value: isVisible)
|
|
759
|
+
}
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
### Avoid Re-creating Views During Animation
|
|
763
|
+
|
|
764
|
+
Ensure animated views maintain stable identity. Use explicit `id()` modifiers
|
|
765
|
+
or stable `ForEach` identifiers.
|
|
766
|
+
|
|
767
|
+
```swift
|
|
768
|
+
// WRONG: View identity changes, breaks animation
|
|
769
|
+
ForEach(Array(items.enumerated()), id: \.offset) { index, item in
|
|
770
|
+
ItemView(item: item)
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// CORRECT: Stable identity from model
|
|
774
|
+
ForEach(items) { item in
|
|
775
|
+
ItemView(item: item)
|
|
776
|
+
}
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
### Use geometryGroup() for Nested Geometry
|
|
780
|
+
|
|
781
|
+
Isolate child geometry from parent animations when they conflict.
|
|
782
|
+
|
|
783
|
+
```swift
|
|
784
|
+
ParentView()
|
|
785
|
+
.scaleEffect(parentScale)
|
|
786
|
+
.geometryGroup() // children see stable geometry
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
### Transaction for Selective Animation Override
|
|
790
|
+
|
|
791
|
+
Override animation for specific subtrees without affecting siblings.
|
|
792
|
+
|
|
793
|
+
```swift
|
|
794
|
+
// Disable animation on one child while parent animates
|
|
795
|
+
ChildView()
|
|
796
|
+
.transaction { $0.animation = nil }
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
### Profile with Instruments
|
|
800
|
+
|
|
801
|
+
Use the Core Animation instrument in Xcode Instruments to verify:
|
|
802
|
+
- Frame rate stays at 120 fps (ProMotion devices) or 60 fps.
|
|
803
|
+
- No offscreen rendering passes.
|
|
804
|
+
- GPU utilization stays reasonable during animations.
|