@devo-bmad-custom/agent-orchestration 1.0.2 → 1.0.3
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 +85 -32
- 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,553 +1,553 @@
|
|
|
1
|
-
# Core Animation Bridge
|
|
2
|
-
|
|
3
|
-
Patterns for bridging Core Animation (QuartzCore) with SwiftUI. Use when SwiftUI's built-in animation system is insufficient -- typically for performance-critical layer animations, unsupported animation curves, or direct CALayer manipulation. Overflow reference for the `swiftui-animation` skill.
|
|
4
|
-
|
|
5
|
-
## Contents
|
|
6
|
-
|
|
7
|
-
- [When to Drop Below SwiftUI Animations](#when-to-drop-below-swiftui-animations)
|
|
8
|
-
- [CABasicAnimation](#cabasicanimation)
|
|
9
|
-
- [CAKeyframeAnimation](#cakeyframeanimation)
|
|
10
|
-
- [CASpringAnimation](#caspringanimation)
|
|
11
|
-
- [CAAnimationGroup](#caanimationgroup)
|
|
12
|
-
- [CADisplayLink](#cadisplaylink)
|
|
13
|
-
- [UIViewRepresentable Wrapper for CA Layers](#uiviewrepresentable-wrapper-for-ca-layers)
|
|
14
|
-
- [Bridging CA Animations with SwiftUI State](#bridging-ca-animations-with-swiftui-state)
|
|
15
|
-
- [Performance Considerations](#performance-considerations)
|
|
16
|
-
|
|
17
|
-
## When to Drop Below SwiftUI Animations
|
|
18
|
-
|
|
19
|
-
SwiftUI's animation system covers most use cases. Drop to Core Animation only when:
|
|
20
|
-
|
|
21
|
-
| Scenario | Why CA Is Needed |
|
|
22
|
-
|----------|-----------------|
|
|
23
|
-
| **Custom timing functions** beyond spring/ease | `CAMediaTimingFunction` supports arbitrary cubic Bezier curves |
|
|
24
|
-
| **Layer-specific properties** (shadowPath, borderWidth, etc.) | SwiftUI does not expose all CALayer animatable properties |
|
|
25
|
-
| **Additive animations** | CA supports additive blending of multiple concurrent animations on the same property |
|
|
26
|
-
| **Frame-synchronized drawing** | `CADisplayLink` provides precise frame timing for custom rendering |
|
|
27
|
-
| **Performance-critical particle/effects** | Direct layer manipulation avoids SwiftUI's diffing overhead |
|
|
28
|
-
| **Animation along a path** | `CAKeyframeAnimation` supports `CGPath`-based animation paths |
|
|
29
|
-
|
|
30
|
-
If SwiftUI's `withAnimation`, `PhaseAnimator`, or `KeyframeAnimator` can achieve the effect, prefer them. Core Animation bridging adds complexity and requires explicit `UIViewRepresentable` wrappers.
|
|
31
|
-
|
|
32
|
-
## CABasicAnimation
|
|
33
|
-
|
|
34
|
-
[`CABasicAnimation`](https://sosumi.ai/documentation/quartzcore/cabasicanimation) interpolates a single layer property between two values.
|
|
35
|
-
|
|
36
|
-
### Basic Usage
|
|
37
|
-
|
|
38
|
-
```swift
|
|
39
|
-
import QuartzCore
|
|
40
|
-
|
|
41
|
-
let animation = CABasicAnimation(keyPath: "opacity")
|
|
42
|
-
animation.fromValue = 0.0
|
|
43
|
-
animation.toValue = 1.0
|
|
44
|
-
animation.duration = 0.3
|
|
45
|
-
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
|
46
|
-
|
|
47
|
-
// Apply to a layer
|
|
48
|
-
layer.add(animation, forKey: "fadeIn")
|
|
49
|
-
layer.opacity = 1.0 // Set the final model value
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### Custom Bezier Timing
|
|
53
|
-
|
|
54
|
-
```swift
|
|
55
|
-
// Custom cubic Bezier curve -- not available in SwiftUI
|
|
56
|
-
let timingFunction = CAMediaTimingFunction(controlPoints: 0.2, 0.8, 0.2, 1.0)
|
|
57
|
-
|
|
58
|
-
let animation = CABasicAnimation(keyPath: "position.y")
|
|
59
|
-
animation.fromValue = layer.position.y
|
|
60
|
-
animation.toValue = layer.position.y - 100
|
|
61
|
-
animation.duration = 0.5
|
|
62
|
-
animation.timingFunction = timingFunction
|
|
63
|
-
animation.fillMode = .forwards
|
|
64
|
-
animation.isRemovedOnCompletion = false
|
|
65
|
-
|
|
66
|
-
layer.add(animation, forKey: "customBezier")
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### Shadow Path Animation
|
|
70
|
-
|
|
71
|
-
```swift
|
|
72
|
-
// Animate shadowPath -- not possible in pure SwiftUI
|
|
73
|
-
let animation = CABasicAnimation(keyPath: "shadowPath")
|
|
74
|
-
animation.fromValue = layer.shadowPath
|
|
75
|
-
animation.toValue = UIBezierPath(roundedRect: newBounds, cornerRadius: 16).cgPath
|
|
76
|
-
animation.duration = 0.3
|
|
77
|
-
animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
|
78
|
-
|
|
79
|
-
layer.shadowPath = UIBezierPath(roundedRect: newBounds, cornerRadius: 16).cgPath
|
|
80
|
-
layer.add(animation, forKey: "shadowPath")
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
**Important:** Always set the model value (the property on the layer itself) to the final state. Core Animation operates on a separate presentation layer -- without setting the model value, the layer snaps back when the animation completes.
|
|
84
|
-
|
|
85
|
-
> **Docs:** [CABasicAnimation](https://sosumi.ai/documentation/quartzcore/cabasicanimation) | [CAMediaTimingFunction](https://sosumi.ai/documentation/quartzcore/camediatimingfunction)
|
|
86
|
-
|
|
87
|
-
## CAKeyframeAnimation
|
|
88
|
-
|
|
89
|
-
[`CAKeyframeAnimation`](https://sosumi.ai/documentation/quartzcore/cakeyframeanimation) animates a property through a sequence of values or along a path.
|
|
90
|
-
|
|
91
|
-
### Value-Based Keyframes
|
|
92
|
-
|
|
93
|
-
```swift
|
|
94
|
-
let animation = CAKeyframeAnimation(keyPath: "transform.scale")
|
|
95
|
-
animation.values = [1.0, 1.3, 0.9, 1.05, 1.0]
|
|
96
|
-
animation.keyTimes = [0, 0.25, 0.5, 0.75, 1.0] // Normalized [0..1]
|
|
97
|
-
animation.duration = 0.6
|
|
98
|
-
animation.timingFunctions = [
|
|
99
|
-
CAMediaTimingFunction(name: .easeOut),
|
|
100
|
-
CAMediaTimingFunction(name: .easeIn),
|
|
101
|
-
CAMediaTimingFunction(name: .easeOut),
|
|
102
|
-
CAMediaTimingFunction(name: .easeInEaseOut)
|
|
103
|
-
]
|
|
104
|
-
|
|
105
|
-
layer.add(animation, forKey: "bounceScale")
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### Path-Based Animation
|
|
109
|
-
|
|
110
|
-
```swift
|
|
111
|
-
// Animate position along a CGPath -- unique to CAKeyframeAnimation
|
|
112
|
-
let path = CGMutablePath()
|
|
113
|
-
path.move(to: CGPoint(x: 50, y: 300))
|
|
114
|
-
path.addCurve(
|
|
115
|
-
to: CGPoint(x: 300, y: 50),
|
|
116
|
-
control1: CGPoint(x: 100, y: 50),
|
|
117
|
-
control2: CGPoint(x: 250, y: 300)
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
let animation = CAKeyframeAnimation(keyPath: "position")
|
|
121
|
-
animation.path = path
|
|
122
|
-
animation.duration = 1.5
|
|
123
|
-
animation.rotationMode = .rotateAuto // Rotate along the tangent
|
|
124
|
-
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
|
125
|
-
|
|
126
|
-
layer.add(animation, forKey: "pathAnimation")
|
|
127
|
-
layer.position = CGPoint(x: 300, y: 50)
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Shake Animation (Discrete Keyframes)
|
|
131
|
-
|
|
132
|
-
```swift
|
|
133
|
-
func shakeAnimation() -> CAKeyframeAnimation {
|
|
134
|
-
let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
|
|
135
|
-
animation.values = [0, -10, 10, -8, 8, -5, 5, 0]
|
|
136
|
-
animation.keyTimes = [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 1.0]
|
|
137
|
-
animation.duration = 0.5
|
|
138
|
-
animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
|
139
|
-
return animation
|
|
140
|
-
}
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
> **Docs:** [CAKeyframeAnimation](https://sosumi.ai/documentation/quartzcore/cakeyframeanimation)
|
|
144
|
-
|
|
145
|
-
## CASpringAnimation
|
|
146
|
-
|
|
147
|
-
[`CASpringAnimation`](https://sosumi.ai/documentation/quartzcore/caspringanimation) applies spring physics to a layer property. It extends `CABasicAnimation` with physical spring attributes.
|
|
148
|
-
|
|
149
|
-
### Physical Spring Parameters
|
|
150
|
-
|
|
151
|
-
```swift
|
|
152
|
-
let spring = CASpringAnimation(keyPath: "transform.scale")
|
|
153
|
-
spring.fromValue = 0.0
|
|
154
|
-
spring.toValue = 1.0
|
|
155
|
-
spring.mass = 1.0
|
|
156
|
-
spring.stiffness = 200.0
|
|
157
|
-
spring.damping = 10.0
|
|
158
|
-
spring.initialVelocity = 0.0
|
|
159
|
-
spring.duration = spring.settlingDuration // Use the physics-calculated duration
|
|
160
|
-
|
|
161
|
-
layer.add(spring, forKey: "springScale")
|
|
162
|
-
layer.transform = CATransform3DIdentity
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### Perceptual Spring (iOS 17+)
|
|
166
|
-
|
|
167
|
-
```swift
|
|
168
|
-
let spring = CASpringAnimation(perceptualDuration: 0.5, bounce: 0.3)
|
|
169
|
-
spring.keyPath = "position.y"
|
|
170
|
-
spring.fromValue = layer.position.y
|
|
171
|
-
spring.toValue = layer.position.y - 100
|
|
172
|
-
|
|
173
|
-
layer.add(spring, forKey: "perceptualSpring")
|
|
174
|
-
layer.position.y -= 100
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
The `perceptualDuration` and `bounce` initializer matches SwiftUI's `Spring(duration:bounce:)`, making it easier to keep CA and SwiftUI spring behaviors consistent.
|
|
178
|
-
|
|
179
|
-
### Matching SwiftUI Spring Presets
|
|
180
|
-
|
|
181
|
-
| SwiftUI Preset | CA Equivalent |
|
|
182
|
-
|---------------|---------------|
|
|
183
|
-
| `.smooth` | `CASpringAnimation(perceptualDuration: 0.5, bounce: 0.0)` |
|
|
184
|
-
| `.snappy` | `CASpringAnimation(perceptualDuration: 0.4, bounce: 0.15)` |
|
|
185
|
-
| `.bouncy` | `CASpringAnimation(perceptualDuration: 0.5, bounce: 0.3)` |
|
|
186
|
-
|
|
187
|
-
> **Docs:** [CASpringAnimation](https://sosumi.ai/documentation/quartzcore/caspringanimation)
|
|
188
|
-
|
|
189
|
-
## CAAnimationGroup
|
|
190
|
-
|
|
191
|
-
[`CAAnimationGroup`](https://sosumi.ai/documentation/quartzcore/caanimationgroup) runs multiple animations concurrently on the same layer.
|
|
192
|
-
|
|
193
|
-
```swift
|
|
194
|
-
let scaleAnim = CABasicAnimation(keyPath: "transform.scale")
|
|
195
|
-
scaleAnim.fromValue = 0.5
|
|
196
|
-
scaleAnim.toValue = 1.0
|
|
197
|
-
|
|
198
|
-
let opacityAnim = CABasicAnimation(keyPath: "opacity")
|
|
199
|
-
opacityAnim.fromValue = 0.0
|
|
200
|
-
opacityAnim.toValue = 1.0
|
|
201
|
-
|
|
202
|
-
let group = CAAnimationGroup()
|
|
203
|
-
group.animations = [scaleAnim, opacityAnim]
|
|
204
|
-
group.duration = 0.4
|
|
205
|
-
group.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
|
206
|
-
|
|
207
|
-
layer.add(group, forKey: "appearGroup")
|
|
208
|
-
layer.transform = CATransform3DIdentity
|
|
209
|
-
layer.opacity = 1.0
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
> **Docs:** [CAAnimationGroup](https://sosumi.ai/documentation/quartzcore/caanimationgroup)
|
|
213
|
-
|
|
214
|
-
## CADisplayLink
|
|
215
|
-
|
|
216
|
-
[`CADisplayLink`](https://sosumi.ai/documentation/quartzcore/cadisplaylink) is a timer synchronized to the display's refresh rate. Use it for frame-accurate custom drawing, particle systems, or manual animation loops.
|
|
217
|
-
|
|
218
|
-
### Basic Display Link
|
|
219
|
-
|
|
220
|
-
```swift
|
|
221
|
-
import QuartzCore
|
|
222
|
-
|
|
223
|
-
final class FrameAnimator {
|
|
224
|
-
private var displayLink: CADisplayLink?
|
|
225
|
-
private var startTime: CFTimeInterval = 0
|
|
226
|
-
|
|
227
|
-
func start() {
|
|
228
|
-
displayLink = CADisplayLink(target: self, selector: #selector(onFrame))
|
|
229
|
-
displayLink?.add(to: .main, forMode: .common)
|
|
230
|
-
startTime = CACurrentMediaTime()
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
func stop() {
|
|
234
|
-
displayLink?.invalidate()
|
|
235
|
-
displayLink = nil
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
@objc private func onFrame(_ link: CADisplayLink) {
|
|
239
|
-
let elapsed = link.timestamp - startTime
|
|
240
|
-
let progress = min(elapsed / 2.0, 1.0) // 2-second animation
|
|
241
|
-
|
|
242
|
-
// Update rendering based on progress
|
|
243
|
-
updateAnimation(progress: progress)
|
|
244
|
-
|
|
245
|
-
if progress >= 1.0 {
|
|
246
|
-
stop()
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
private func updateAnimation(progress: Double) {
|
|
251
|
-
// Custom per-frame rendering logic
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
### ProMotion Frame Rate Control
|
|
257
|
-
|
|
258
|
-
On ProMotion displays (120 Hz), use `preferredFrameRateRange` to balance smoothness and power:
|
|
259
|
-
|
|
260
|
-
```swift
|
|
261
|
-
displayLink?.preferredFrameRateRange = CAFrameRateRange(
|
|
262
|
-
minimum: 30,
|
|
263
|
-
maximum: 120,
|
|
264
|
-
preferred: 60
|
|
265
|
-
)
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
| Range | Use Case |
|
|
269
|
-
|-------|----------|
|
|
270
|
-
| `preferred: 120` | Smooth scrolling, gesture tracking |
|
|
271
|
-
| `preferred: 60` | Standard animations |
|
|
272
|
-
| `preferred: 30` | Ambient/slow animations, power saving |
|
|
273
|
-
|
|
274
|
-
**Important:** Always call `invalidate()` when done. A running `CADisplayLink` prevents the CPU from idling and drains battery.
|
|
275
|
-
|
|
276
|
-
> **Docs:** [CADisplayLink](https://sosumi.ai/documentation/quartzcore/cadisplaylink) | [Optimizing ProMotion refresh rates](https://sosumi.ai/documentation/quartzcore/optimizing-promotion-refresh-rates-for-iphone-13-pro-and-ipad-pro)
|
|
277
|
-
|
|
278
|
-
## UIViewRepresentable Wrapper for CA Layers
|
|
279
|
-
|
|
280
|
-
To use Core Animation layers inside SwiftUI, wrap them in a `UIViewRepresentable`.
|
|
281
|
-
|
|
282
|
-
### Animated Layer View
|
|
283
|
-
|
|
284
|
-
```swift
|
|
285
|
-
import SwiftUI
|
|
286
|
-
import QuartzCore
|
|
287
|
-
|
|
288
|
-
struct AnimatedLayerView: UIViewRepresentable {
|
|
289
|
-
var isAnimating: Bool
|
|
290
|
-
var color: Color
|
|
291
|
-
|
|
292
|
-
func makeUIView(context: Context) -> AnimatedLayerUIView {
|
|
293
|
-
let view = AnimatedLayerUIView()
|
|
294
|
-
return view
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
func updateUIView(_ uiView: AnimatedLayerUIView, context: Context) {
|
|
298
|
-
uiView.updateColor(UIColor(color))
|
|
299
|
-
|
|
300
|
-
if isAnimating {
|
|
301
|
-
uiView.startAnimation()
|
|
302
|
-
} else {
|
|
303
|
-
uiView.stopAnimation()
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
static func dismantleUIView(_ uiView: AnimatedLayerUIView, coordinator: ()) {
|
|
308
|
-
uiView.stopAnimation()
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
### The Backing UIView
|
|
314
|
-
|
|
315
|
-
```swift
|
|
316
|
-
final class AnimatedLayerUIView: UIView {
|
|
317
|
-
private let animationLayer = CAShapeLayer()
|
|
318
|
-
private var displayLink: CADisplayLink?
|
|
319
|
-
private var phase: CGFloat = 0
|
|
320
|
-
|
|
321
|
-
override init(frame: CGRect) {
|
|
322
|
-
super.init(frame: frame)
|
|
323
|
-
setupLayer()
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
required init?(coder: NSCoder) {
|
|
327
|
-
super.init(coder: coder)
|
|
328
|
-
setupLayer()
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
private func setupLayer() {
|
|
332
|
-
animationLayer.fillColor = UIColor.systemBlue.cgColor
|
|
333
|
-
animationLayer.strokeColor = nil
|
|
334
|
-
layer.addSublayer(animationLayer)
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
override func layoutSubviews() {
|
|
338
|
-
super.layoutSubviews()
|
|
339
|
-
animationLayer.frame = bounds
|
|
340
|
-
updatePath()
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
func updateColor(_ color: UIColor) {
|
|
344
|
-
// Animate color change at the CA layer level
|
|
345
|
-
let animation = CABasicAnimation(keyPath: "fillColor")
|
|
346
|
-
animation.fromValue = animationLayer.fillColor
|
|
347
|
-
animation.toValue = color.cgColor
|
|
348
|
-
animation.duration = 0.3
|
|
349
|
-
|
|
350
|
-
animationLayer.fillColor = color.cgColor
|
|
351
|
-
animationLayer.add(animation, forKey: "colorChange")
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
func startAnimation() {
|
|
355
|
-
guard displayLink == nil else { return }
|
|
356
|
-
displayLink = CADisplayLink(target: self, selector: #selector(tick))
|
|
357
|
-
displayLink?.preferredFrameRateRange = CAFrameRateRange(
|
|
358
|
-
minimum: 30, maximum: 60, preferred: 60
|
|
359
|
-
)
|
|
360
|
-
displayLink?.add(to: .main, forMode: .common)
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
func stopAnimation() {
|
|
364
|
-
displayLink?.invalidate()
|
|
365
|
-
displayLink = nil
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
@objc private func tick(_ link: CADisplayLink) {
|
|
369
|
-
phase += 0.05
|
|
370
|
-
updatePath()
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
private func updatePath() {
|
|
374
|
-
let path = CGMutablePath()
|
|
375
|
-
let width = bounds.width
|
|
376
|
-
let height = bounds.height
|
|
377
|
-
let midY = height / 2
|
|
378
|
-
|
|
379
|
-
path.move(to: CGPoint(x: 0, y: midY))
|
|
380
|
-
for x in stride(from: 0, to: width, by: 2) {
|
|
381
|
-
let relativeX = x / width
|
|
382
|
-
let y = midY + sin((relativeX * .pi * 4) + phase) * (height * 0.3)
|
|
383
|
-
path.addLine(to: CGPoint(x: x, y: y))
|
|
384
|
-
}
|
|
385
|
-
path.addLine(to: CGPoint(x: width, y: height))
|
|
386
|
-
path.addLine(to: CGPoint(x: 0, y: height))
|
|
387
|
-
path.closeSubpath()
|
|
388
|
-
|
|
389
|
-
animationLayer.path = path
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
### SwiftUI Usage
|
|
395
|
-
|
|
396
|
-
```swift
|
|
397
|
-
struct WaveView: View {
|
|
398
|
-
@State private var isAnimating = true
|
|
399
|
-
|
|
400
|
-
var body: some View {
|
|
401
|
-
AnimatedLayerView(isAnimating: isAnimating, color: .blue)
|
|
402
|
-
.frame(height: 200)
|
|
403
|
-
.clipShape(RoundedRectangle(cornerRadius: 16))
|
|
404
|
-
.onTapGesture {
|
|
405
|
-
isAnimating.toggle()
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
### Key Rules for CA-in-SwiftUI Wrappers
|
|
412
|
-
|
|
413
|
-
1. **Create layers in `makeUIView` or the UIView subclass initializer**, not in `updateUIView`.
|
|
414
|
-
2. **Stop display links in `dismantleUIView`** to prevent leaks and background CPU usage.
|
|
415
|
-
3. **Guard against redundant animation starts** in `updateUIView` -- it runs on every SwiftUI state change.
|
|
416
|
-
4. **Set model values alongside CA animations** so the layer state is correct after animations complete.
|
|
417
|
-
|
|
418
|
-
## Bridging CA Animations with SwiftUI State
|
|
419
|
-
|
|
420
|
-
### Triggering CA Animations from SwiftUI State Changes
|
|
421
|
-
|
|
422
|
-
```swift
|
|
423
|
-
struct PulseButton: UIViewRepresentable {
|
|
424
|
-
var pulseCount: Int // Increment to trigger a pulse
|
|
425
|
-
|
|
426
|
-
func makeUIView(context: Context) -> PulseUIView {
|
|
427
|
-
PulseUIView()
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
func updateUIView(_ uiView: PulseUIView, context: Context) {
|
|
431
|
-
// Only animate when pulseCount changes, not on every update
|
|
432
|
-
if context.coordinator.lastPulseCount != pulseCount {
|
|
433
|
-
context.coordinator.lastPulseCount = pulseCount
|
|
434
|
-
uiView.pulse()
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
func makeCoordinator() -> Coordinator { Coordinator() }
|
|
439
|
-
|
|
440
|
-
final class Coordinator {
|
|
441
|
-
var lastPulseCount = 0
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
final class PulseUIView: UIView {
|
|
446
|
-
private let pulseLayer = CAShapeLayer()
|
|
447
|
-
|
|
448
|
-
override init(frame: CGRect) {
|
|
449
|
-
super.init(frame: frame)
|
|
450
|
-
pulseLayer.fillColor = UIColor.systemBlue.withAlphaComponent(0.3).cgColor
|
|
451
|
-
layer.addSublayer(pulseLayer)
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
required init?(coder: NSCoder) { fatalError() }
|
|
455
|
-
|
|
456
|
-
override func layoutSubviews() {
|
|
457
|
-
super.layoutSubviews()
|
|
458
|
-
let size = min(bounds.width, bounds.height)
|
|
459
|
-
let rect = CGRect(
|
|
460
|
-
x: (bounds.width - size) / 2,
|
|
461
|
-
y: (bounds.height - size) / 2,
|
|
462
|
-
width: size,
|
|
463
|
-
height: size
|
|
464
|
-
)
|
|
465
|
-
pulseLayer.path = UIBezierPath(ovalIn: rect).cgPath
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
func pulse() {
|
|
469
|
-
let scaleAnim = CABasicAnimation(keyPath: "transform.scale")
|
|
470
|
-
scaleAnim.fromValue = 1.0
|
|
471
|
-
scaleAnim.toValue = 1.5
|
|
472
|
-
|
|
473
|
-
let opacityAnim = CABasicAnimation(keyPath: "opacity")
|
|
474
|
-
opacityAnim.fromValue = 1.0
|
|
475
|
-
opacityAnim.toValue = 0.0
|
|
476
|
-
|
|
477
|
-
let group = CAAnimationGroup()
|
|
478
|
-
group.animations = [scaleAnim, opacityAnim]
|
|
479
|
-
group.duration = 0.6
|
|
480
|
-
group.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
|
481
|
-
|
|
482
|
-
pulseLayer.add(group, forKey: "pulse")
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
```
|
|
486
|
-
|
|
487
|
-
### Reading CA Animation Completion in SwiftUI
|
|
488
|
-
|
|
489
|
-
Use `CAAnimationDelegate` on the Coordinator to report animation completion back to SwiftUI:
|
|
490
|
-
|
|
491
|
-
```swift
|
|
492
|
-
struct AnimatedBadge: UIViewRepresentable {
|
|
493
|
-
@Binding var isAnimationComplete: Bool
|
|
494
|
-
|
|
495
|
-
func makeCoordinator() -> Coordinator { Coordinator(self) }
|
|
496
|
-
|
|
497
|
-
func makeUIView(context: Context) -> UIView {
|
|
498
|
-
let view = UIView()
|
|
499
|
-
let badge = CAShapeLayer()
|
|
500
|
-
badge.path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 40, height: 40)).cgPath
|
|
501
|
-
badge.fillColor = UIColor.systemRed.cgColor
|
|
502
|
-
badge.name = "badge"
|
|
503
|
-
view.layer.addSublayer(badge)
|
|
504
|
-
return view
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
func updateUIView(_ uiView: UIView, context: Context) {}
|
|
508
|
-
|
|
509
|
-
func animateIn(_ uiView: UIView) {
|
|
510
|
-
guard let badge = uiView.layer.sublayers?.first(where: { $0.name == "badge" }) else { return }
|
|
511
|
-
|
|
512
|
-
let spring = CASpringAnimation(perceptualDuration: 0.5, bounce: 0.3)
|
|
513
|
-
spring.keyPath = "transform.scale"
|
|
514
|
-
spring.fromValue = 0.0
|
|
515
|
-
spring.toValue = 1.0
|
|
516
|
-
spring.delegate = uiView.next as? CAAnimationDelegate
|
|
517
|
-
|
|
518
|
-
badge.add(spring, forKey: "appear")
|
|
519
|
-
badge.transform = CATransform3DIdentity
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
final class Coordinator: NSObject, CAAnimationDelegate {
|
|
523
|
-
var parent: AnimatedBadge
|
|
524
|
-
|
|
525
|
-
init(_ parent: AnimatedBadge) { self.parent = parent }
|
|
526
|
-
|
|
527
|
-
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
|
|
528
|
-
if flag {
|
|
529
|
-
parent.isAnimationComplete = true
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
```
|
|
535
|
-
|
|
536
|
-
## Performance Considerations
|
|
537
|
-
|
|
538
|
-
### CA vs. SwiftUI Animation Performance
|
|
539
|
-
|
|
540
|
-
| Aspect | SwiftUI Animation | Core Animation |
|
|
541
|
-
|--------|------------------|----------------|
|
|
542
|
-
| **Rendering** | View diffing + render tree | Direct layer manipulation |
|
|
543
|
-
| **Thread** | Main thread for state, render server for compositing | Same -- render server composites |
|
|
544
|
-
| **Overhead** | SwiftUI body re-evaluation per frame (for animatable) | No body re-evaluation |
|
|
545
|
-
| **Best for** | Standard UI transitions | Particle effects, wave animations, complex paths |
|
|
546
|
-
|
|
547
|
-
### Guidelines
|
|
548
|
-
|
|
549
|
-
- **Avoid mixing CA animations and SwiftUI animations on the same property.** They use separate animation systems and will conflict.
|
|
550
|
-
- **Use `CADisplayLink` sparingly.** A running display link prevents the CPU from sleeping. Always invalidate when not needed.
|
|
551
|
-
- **Prefer `CAShapeLayer` for path-based animations** over redrawing in `draw(_:)`. Shape layers are GPU-accelerated.
|
|
552
|
-
- **Set `shouldRasterize = true`** on complex static sublayer trees to cache them as bitmaps, but disable it during animation (rasterization prevents smooth per-frame updates).
|
|
553
|
-
- **Match CA spring parameters to SwiftUI springs** using the `perceptualDuration:bounce:` initializer so animations feel consistent across the bridge boundary.
|
|
1
|
+
# Core Animation Bridge
|
|
2
|
+
|
|
3
|
+
Patterns for bridging Core Animation (QuartzCore) with SwiftUI. Use when SwiftUI's built-in animation system is insufficient -- typically for performance-critical layer animations, unsupported animation curves, or direct CALayer manipulation. Overflow reference for the `swiftui-animation` skill.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [When to Drop Below SwiftUI Animations](#when-to-drop-below-swiftui-animations)
|
|
8
|
+
- [CABasicAnimation](#cabasicanimation)
|
|
9
|
+
- [CAKeyframeAnimation](#cakeyframeanimation)
|
|
10
|
+
- [CASpringAnimation](#caspringanimation)
|
|
11
|
+
- [CAAnimationGroup](#caanimationgroup)
|
|
12
|
+
- [CADisplayLink](#cadisplaylink)
|
|
13
|
+
- [UIViewRepresentable Wrapper for CA Layers](#uiviewrepresentable-wrapper-for-ca-layers)
|
|
14
|
+
- [Bridging CA Animations with SwiftUI State](#bridging-ca-animations-with-swiftui-state)
|
|
15
|
+
- [Performance Considerations](#performance-considerations)
|
|
16
|
+
|
|
17
|
+
## When to Drop Below SwiftUI Animations
|
|
18
|
+
|
|
19
|
+
SwiftUI's animation system covers most use cases. Drop to Core Animation only when:
|
|
20
|
+
|
|
21
|
+
| Scenario | Why CA Is Needed |
|
|
22
|
+
|----------|-----------------|
|
|
23
|
+
| **Custom timing functions** beyond spring/ease | `CAMediaTimingFunction` supports arbitrary cubic Bezier curves |
|
|
24
|
+
| **Layer-specific properties** (shadowPath, borderWidth, etc.) | SwiftUI does not expose all CALayer animatable properties |
|
|
25
|
+
| **Additive animations** | CA supports additive blending of multiple concurrent animations on the same property |
|
|
26
|
+
| **Frame-synchronized drawing** | `CADisplayLink` provides precise frame timing for custom rendering |
|
|
27
|
+
| **Performance-critical particle/effects** | Direct layer manipulation avoids SwiftUI's diffing overhead |
|
|
28
|
+
| **Animation along a path** | `CAKeyframeAnimation` supports `CGPath`-based animation paths |
|
|
29
|
+
|
|
30
|
+
If SwiftUI's `withAnimation`, `PhaseAnimator`, or `KeyframeAnimator` can achieve the effect, prefer them. Core Animation bridging adds complexity and requires explicit `UIViewRepresentable` wrappers.
|
|
31
|
+
|
|
32
|
+
## CABasicAnimation
|
|
33
|
+
|
|
34
|
+
[`CABasicAnimation`](https://sosumi.ai/documentation/quartzcore/cabasicanimation) interpolates a single layer property between two values.
|
|
35
|
+
|
|
36
|
+
### Basic Usage
|
|
37
|
+
|
|
38
|
+
```swift
|
|
39
|
+
import QuartzCore
|
|
40
|
+
|
|
41
|
+
let animation = CABasicAnimation(keyPath: "opacity")
|
|
42
|
+
animation.fromValue = 0.0
|
|
43
|
+
animation.toValue = 1.0
|
|
44
|
+
animation.duration = 0.3
|
|
45
|
+
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
|
46
|
+
|
|
47
|
+
// Apply to a layer
|
|
48
|
+
layer.add(animation, forKey: "fadeIn")
|
|
49
|
+
layer.opacity = 1.0 // Set the final model value
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Custom Bezier Timing
|
|
53
|
+
|
|
54
|
+
```swift
|
|
55
|
+
// Custom cubic Bezier curve -- not available in SwiftUI
|
|
56
|
+
let timingFunction = CAMediaTimingFunction(controlPoints: 0.2, 0.8, 0.2, 1.0)
|
|
57
|
+
|
|
58
|
+
let animation = CABasicAnimation(keyPath: "position.y")
|
|
59
|
+
animation.fromValue = layer.position.y
|
|
60
|
+
animation.toValue = layer.position.y - 100
|
|
61
|
+
animation.duration = 0.5
|
|
62
|
+
animation.timingFunction = timingFunction
|
|
63
|
+
animation.fillMode = .forwards
|
|
64
|
+
animation.isRemovedOnCompletion = false
|
|
65
|
+
|
|
66
|
+
layer.add(animation, forKey: "customBezier")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Shadow Path Animation
|
|
70
|
+
|
|
71
|
+
```swift
|
|
72
|
+
// Animate shadowPath -- not possible in pure SwiftUI
|
|
73
|
+
let animation = CABasicAnimation(keyPath: "shadowPath")
|
|
74
|
+
animation.fromValue = layer.shadowPath
|
|
75
|
+
animation.toValue = UIBezierPath(roundedRect: newBounds, cornerRadius: 16).cgPath
|
|
76
|
+
animation.duration = 0.3
|
|
77
|
+
animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
|
78
|
+
|
|
79
|
+
layer.shadowPath = UIBezierPath(roundedRect: newBounds, cornerRadius: 16).cgPath
|
|
80
|
+
layer.add(animation, forKey: "shadowPath")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Important:** Always set the model value (the property on the layer itself) to the final state. Core Animation operates on a separate presentation layer -- without setting the model value, the layer snaps back when the animation completes.
|
|
84
|
+
|
|
85
|
+
> **Docs:** [CABasicAnimation](https://sosumi.ai/documentation/quartzcore/cabasicanimation) | [CAMediaTimingFunction](https://sosumi.ai/documentation/quartzcore/camediatimingfunction)
|
|
86
|
+
|
|
87
|
+
## CAKeyframeAnimation
|
|
88
|
+
|
|
89
|
+
[`CAKeyframeAnimation`](https://sosumi.ai/documentation/quartzcore/cakeyframeanimation) animates a property through a sequence of values or along a path.
|
|
90
|
+
|
|
91
|
+
### Value-Based Keyframes
|
|
92
|
+
|
|
93
|
+
```swift
|
|
94
|
+
let animation = CAKeyframeAnimation(keyPath: "transform.scale")
|
|
95
|
+
animation.values = [1.0, 1.3, 0.9, 1.05, 1.0]
|
|
96
|
+
animation.keyTimes = [0, 0.25, 0.5, 0.75, 1.0] // Normalized [0..1]
|
|
97
|
+
animation.duration = 0.6
|
|
98
|
+
animation.timingFunctions = [
|
|
99
|
+
CAMediaTimingFunction(name: .easeOut),
|
|
100
|
+
CAMediaTimingFunction(name: .easeIn),
|
|
101
|
+
CAMediaTimingFunction(name: .easeOut),
|
|
102
|
+
CAMediaTimingFunction(name: .easeInEaseOut)
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
layer.add(animation, forKey: "bounceScale")
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Path-Based Animation
|
|
109
|
+
|
|
110
|
+
```swift
|
|
111
|
+
// Animate position along a CGPath -- unique to CAKeyframeAnimation
|
|
112
|
+
let path = CGMutablePath()
|
|
113
|
+
path.move(to: CGPoint(x: 50, y: 300))
|
|
114
|
+
path.addCurve(
|
|
115
|
+
to: CGPoint(x: 300, y: 50),
|
|
116
|
+
control1: CGPoint(x: 100, y: 50),
|
|
117
|
+
control2: CGPoint(x: 250, y: 300)
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
let animation = CAKeyframeAnimation(keyPath: "position")
|
|
121
|
+
animation.path = path
|
|
122
|
+
animation.duration = 1.5
|
|
123
|
+
animation.rotationMode = .rotateAuto // Rotate along the tangent
|
|
124
|
+
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
|
125
|
+
|
|
126
|
+
layer.add(animation, forKey: "pathAnimation")
|
|
127
|
+
layer.position = CGPoint(x: 300, y: 50)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Shake Animation (Discrete Keyframes)
|
|
131
|
+
|
|
132
|
+
```swift
|
|
133
|
+
func shakeAnimation() -> CAKeyframeAnimation {
|
|
134
|
+
let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
|
|
135
|
+
animation.values = [0, -10, 10, -8, 8, -5, 5, 0]
|
|
136
|
+
animation.keyTimes = [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 1.0]
|
|
137
|
+
animation.duration = 0.5
|
|
138
|
+
animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
|
139
|
+
return animation
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
> **Docs:** [CAKeyframeAnimation](https://sosumi.ai/documentation/quartzcore/cakeyframeanimation)
|
|
144
|
+
|
|
145
|
+
## CASpringAnimation
|
|
146
|
+
|
|
147
|
+
[`CASpringAnimation`](https://sosumi.ai/documentation/quartzcore/caspringanimation) applies spring physics to a layer property. It extends `CABasicAnimation` with physical spring attributes.
|
|
148
|
+
|
|
149
|
+
### Physical Spring Parameters
|
|
150
|
+
|
|
151
|
+
```swift
|
|
152
|
+
let spring = CASpringAnimation(keyPath: "transform.scale")
|
|
153
|
+
spring.fromValue = 0.0
|
|
154
|
+
spring.toValue = 1.0
|
|
155
|
+
spring.mass = 1.0
|
|
156
|
+
spring.stiffness = 200.0
|
|
157
|
+
spring.damping = 10.0
|
|
158
|
+
spring.initialVelocity = 0.0
|
|
159
|
+
spring.duration = spring.settlingDuration // Use the physics-calculated duration
|
|
160
|
+
|
|
161
|
+
layer.add(spring, forKey: "springScale")
|
|
162
|
+
layer.transform = CATransform3DIdentity
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Perceptual Spring (iOS 17+)
|
|
166
|
+
|
|
167
|
+
```swift
|
|
168
|
+
let spring = CASpringAnimation(perceptualDuration: 0.5, bounce: 0.3)
|
|
169
|
+
spring.keyPath = "position.y"
|
|
170
|
+
spring.fromValue = layer.position.y
|
|
171
|
+
spring.toValue = layer.position.y - 100
|
|
172
|
+
|
|
173
|
+
layer.add(spring, forKey: "perceptualSpring")
|
|
174
|
+
layer.position.y -= 100
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The `perceptualDuration` and `bounce` initializer matches SwiftUI's `Spring(duration:bounce:)`, making it easier to keep CA and SwiftUI spring behaviors consistent.
|
|
178
|
+
|
|
179
|
+
### Matching SwiftUI Spring Presets
|
|
180
|
+
|
|
181
|
+
| SwiftUI Preset | CA Equivalent |
|
|
182
|
+
|---------------|---------------|
|
|
183
|
+
| `.smooth` | `CASpringAnimation(perceptualDuration: 0.5, bounce: 0.0)` |
|
|
184
|
+
| `.snappy` | `CASpringAnimation(perceptualDuration: 0.4, bounce: 0.15)` |
|
|
185
|
+
| `.bouncy` | `CASpringAnimation(perceptualDuration: 0.5, bounce: 0.3)` |
|
|
186
|
+
|
|
187
|
+
> **Docs:** [CASpringAnimation](https://sosumi.ai/documentation/quartzcore/caspringanimation)
|
|
188
|
+
|
|
189
|
+
## CAAnimationGroup
|
|
190
|
+
|
|
191
|
+
[`CAAnimationGroup`](https://sosumi.ai/documentation/quartzcore/caanimationgroup) runs multiple animations concurrently on the same layer.
|
|
192
|
+
|
|
193
|
+
```swift
|
|
194
|
+
let scaleAnim = CABasicAnimation(keyPath: "transform.scale")
|
|
195
|
+
scaleAnim.fromValue = 0.5
|
|
196
|
+
scaleAnim.toValue = 1.0
|
|
197
|
+
|
|
198
|
+
let opacityAnim = CABasicAnimation(keyPath: "opacity")
|
|
199
|
+
opacityAnim.fromValue = 0.0
|
|
200
|
+
opacityAnim.toValue = 1.0
|
|
201
|
+
|
|
202
|
+
let group = CAAnimationGroup()
|
|
203
|
+
group.animations = [scaleAnim, opacityAnim]
|
|
204
|
+
group.duration = 0.4
|
|
205
|
+
group.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
|
206
|
+
|
|
207
|
+
layer.add(group, forKey: "appearGroup")
|
|
208
|
+
layer.transform = CATransform3DIdentity
|
|
209
|
+
layer.opacity = 1.0
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
> **Docs:** [CAAnimationGroup](https://sosumi.ai/documentation/quartzcore/caanimationgroup)
|
|
213
|
+
|
|
214
|
+
## CADisplayLink
|
|
215
|
+
|
|
216
|
+
[`CADisplayLink`](https://sosumi.ai/documentation/quartzcore/cadisplaylink) is a timer synchronized to the display's refresh rate. Use it for frame-accurate custom drawing, particle systems, or manual animation loops.
|
|
217
|
+
|
|
218
|
+
### Basic Display Link
|
|
219
|
+
|
|
220
|
+
```swift
|
|
221
|
+
import QuartzCore
|
|
222
|
+
|
|
223
|
+
final class FrameAnimator {
|
|
224
|
+
private var displayLink: CADisplayLink?
|
|
225
|
+
private var startTime: CFTimeInterval = 0
|
|
226
|
+
|
|
227
|
+
func start() {
|
|
228
|
+
displayLink = CADisplayLink(target: self, selector: #selector(onFrame))
|
|
229
|
+
displayLink?.add(to: .main, forMode: .common)
|
|
230
|
+
startTime = CACurrentMediaTime()
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
func stop() {
|
|
234
|
+
displayLink?.invalidate()
|
|
235
|
+
displayLink = nil
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
@objc private func onFrame(_ link: CADisplayLink) {
|
|
239
|
+
let elapsed = link.timestamp - startTime
|
|
240
|
+
let progress = min(elapsed / 2.0, 1.0) // 2-second animation
|
|
241
|
+
|
|
242
|
+
// Update rendering based on progress
|
|
243
|
+
updateAnimation(progress: progress)
|
|
244
|
+
|
|
245
|
+
if progress >= 1.0 {
|
|
246
|
+
stop()
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private func updateAnimation(progress: Double) {
|
|
251
|
+
// Custom per-frame rendering logic
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### ProMotion Frame Rate Control
|
|
257
|
+
|
|
258
|
+
On ProMotion displays (120 Hz), use `preferredFrameRateRange` to balance smoothness and power:
|
|
259
|
+
|
|
260
|
+
```swift
|
|
261
|
+
displayLink?.preferredFrameRateRange = CAFrameRateRange(
|
|
262
|
+
minimum: 30,
|
|
263
|
+
maximum: 120,
|
|
264
|
+
preferred: 60
|
|
265
|
+
)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
| Range | Use Case |
|
|
269
|
+
|-------|----------|
|
|
270
|
+
| `preferred: 120` | Smooth scrolling, gesture tracking |
|
|
271
|
+
| `preferred: 60` | Standard animations |
|
|
272
|
+
| `preferred: 30` | Ambient/slow animations, power saving |
|
|
273
|
+
|
|
274
|
+
**Important:** Always call `invalidate()` when done. A running `CADisplayLink` prevents the CPU from idling and drains battery.
|
|
275
|
+
|
|
276
|
+
> **Docs:** [CADisplayLink](https://sosumi.ai/documentation/quartzcore/cadisplaylink) | [Optimizing ProMotion refresh rates](https://sosumi.ai/documentation/quartzcore/optimizing-promotion-refresh-rates-for-iphone-13-pro-and-ipad-pro)
|
|
277
|
+
|
|
278
|
+
## UIViewRepresentable Wrapper for CA Layers
|
|
279
|
+
|
|
280
|
+
To use Core Animation layers inside SwiftUI, wrap them in a `UIViewRepresentable`.
|
|
281
|
+
|
|
282
|
+
### Animated Layer View
|
|
283
|
+
|
|
284
|
+
```swift
|
|
285
|
+
import SwiftUI
|
|
286
|
+
import QuartzCore
|
|
287
|
+
|
|
288
|
+
struct AnimatedLayerView: UIViewRepresentable {
|
|
289
|
+
var isAnimating: Bool
|
|
290
|
+
var color: Color
|
|
291
|
+
|
|
292
|
+
func makeUIView(context: Context) -> AnimatedLayerUIView {
|
|
293
|
+
let view = AnimatedLayerUIView()
|
|
294
|
+
return view
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
func updateUIView(_ uiView: AnimatedLayerUIView, context: Context) {
|
|
298
|
+
uiView.updateColor(UIColor(color))
|
|
299
|
+
|
|
300
|
+
if isAnimating {
|
|
301
|
+
uiView.startAnimation()
|
|
302
|
+
} else {
|
|
303
|
+
uiView.stopAnimation()
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
static func dismantleUIView(_ uiView: AnimatedLayerUIView, coordinator: ()) {
|
|
308
|
+
uiView.stopAnimation()
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### The Backing UIView
|
|
314
|
+
|
|
315
|
+
```swift
|
|
316
|
+
final class AnimatedLayerUIView: UIView {
|
|
317
|
+
private let animationLayer = CAShapeLayer()
|
|
318
|
+
private var displayLink: CADisplayLink?
|
|
319
|
+
private var phase: CGFloat = 0
|
|
320
|
+
|
|
321
|
+
override init(frame: CGRect) {
|
|
322
|
+
super.init(frame: frame)
|
|
323
|
+
setupLayer()
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
required init?(coder: NSCoder) {
|
|
327
|
+
super.init(coder: coder)
|
|
328
|
+
setupLayer()
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
private func setupLayer() {
|
|
332
|
+
animationLayer.fillColor = UIColor.systemBlue.cgColor
|
|
333
|
+
animationLayer.strokeColor = nil
|
|
334
|
+
layer.addSublayer(animationLayer)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
override func layoutSubviews() {
|
|
338
|
+
super.layoutSubviews()
|
|
339
|
+
animationLayer.frame = bounds
|
|
340
|
+
updatePath()
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
func updateColor(_ color: UIColor) {
|
|
344
|
+
// Animate color change at the CA layer level
|
|
345
|
+
let animation = CABasicAnimation(keyPath: "fillColor")
|
|
346
|
+
animation.fromValue = animationLayer.fillColor
|
|
347
|
+
animation.toValue = color.cgColor
|
|
348
|
+
animation.duration = 0.3
|
|
349
|
+
|
|
350
|
+
animationLayer.fillColor = color.cgColor
|
|
351
|
+
animationLayer.add(animation, forKey: "colorChange")
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
func startAnimation() {
|
|
355
|
+
guard displayLink == nil else { return }
|
|
356
|
+
displayLink = CADisplayLink(target: self, selector: #selector(tick))
|
|
357
|
+
displayLink?.preferredFrameRateRange = CAFrameRateRange(
|
|
358
|
+
minimum: 30, maximum: 60, preferred: 60
|
|
359
|
+
)
|
|
360
|
+
displayLink?.add(to: .main, forMode: .common)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
func stopAnimation() {
|
|
364
|
+
displayLink?.invalidate()
|
|
365
|
+
displayLink = nil
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
@objc private func tick(_ link: CADisplayLink) {
|
|
369
|
+
phase += 0.05
|
|
370
|
+
updatePath()
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private func updatePath() {
|
|
374
|
+
let path = CGMutablePath()
|
|
375
|
+
let width = bounds.width
|
|
376
|
+
let height = bounds.height
|
|
377
|
+
let midY = height / 2
|
|
378
|
+
|
|
379
|
+
path.move(to: CGPoint(x: 0, y: midY))
|
|
380
|
+
for x in stride(from: 0, to: width, by: 2) {
|
|
381
|
+
let relativeX = x / width
|
|
382
|
+
let y = midY + sin((relativeX * .pi * 4) + phase) * (height * 0.3)
|
|
383
|
+
path.addLine(to: CGPoint(x: x, y: y))
|
|
384
|
+
}
|
|
385
|
+
path.addLine(to: CGPoint(x: width, y: height))
|
|
386
|
+
path.addLine(to: CGPoint(x: 0, y: height))
|
|
387
|
+
path.closeSubpath()
|
|
388
|
+
|
|
389
|
+
animationLayer.path = path
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### SwiftUI Usage
|
|
395
|
+
|
|
396
|
+
```swift
|
|
397
|
+
struct WaveView: View {
|
|
398
|
+
@State private var isAnimating = true
|
|
399
|
+
|
|
400
|
+
var body: some View {
|
|
401
|
+
AnimatedLayerView(isAnimating: isAnimating, color: .blue)
|
|
402
|
+
.frame(height: 200)
|
|
403
|
+
.clipShape(RoundedRectangle(cornerRadius: 16))
|
|
404
|
+
.onTapGesture {
|
|
405
|
+
isAnimating.toggle()
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Key Rules for CA-in-SwiftUI Wrappers
|
|
412
|
+
|
|
413
|
+
1. **Create layers in `makeUIView` or the UIView subclass initializer**, not in `updateUIView`.
|
|
414
|
+
2. **Stop display links in `dismantleUIView`** to prevent leaks and background CPU usage.
|
|
415
|
+
3. **Guard against redundant animation starts** in `updateUIView` -- it runs on every SwiftUI state change.
|
|
416
|
+
4. **Set model values alongside CA animations** so the layer state is correct after animations complete.
|
|
417
|
+
|
|
418
|
+
## Bridging CA Animations with SwiftUI State
|
|
419
|
+
|
|
420
|
+
### Triggering CA Animations from SwiftUI State Changes
|
|
421
|
+
|
|
422
|
+
```swift
|
|
423
|
+
struct PulseButton: UIViewRepresentable {
|
|
424
|
+
var pulseCount: Int // Increment to trigger a pulse
|
|
425
|
+
|
|
426
|
+
func makeUIView(context: Context) -> PulseUIView {
|
|
427
|
+
PulseUIView()
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
func updateUIView(_ uiView: PulseUIView, context: Context) {
|
|
431
|
+
// Only animate when pulseCount changes, not on every update
|
|
432
|
+
if context.coordinator.lastPulseCount != pulseCount {
|
|
433
|
+
context.coordinator.lastPulseCount = pulseCount
|
|
434
|
+
uiView.pulse()
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
func makeCoordinator() -> Coordinator { Coordinator() }
|
|
439
|
+
|
|
440
|
+
final class Coordinator {
|
|
441
|
+
var lastPulseCount = 0
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
final class PulseUIView: UIView {
|
|
446
|
+
private let pulseLayer = CAShapeLayer()
|
|
447
|
+
|
|
448
|
+
override init(frame: CGRect) {
|
|
449
|
+
super.init(frame: frame)
|
|
450
|
+
pulseLayer.fillColor = UIColor.systemBlue.withAlphaComponent(0.3).cgColor
|
|
451
|
+
layer.addSublayer(pulseLayer)
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
required init?(coder: NSCoder) { fatalError() }
|
|
455
|
+
|
|
456
|
+
override func layoutSubviews() {
|
|
457
|
+
super.layoutSubviews()
|
|
458
|
+
let size = min(bounds.width, bounds.height)
|
|
459
|
+
let rect = CGRect(
|
|
460
|
+
x: (bounds.width - size) / 2,
|
|
461
|
+
y: (bounds.height - size) / 2,
|
|
462
|
+
width: size,
|
|
463
|
+
height: size
|
|
464
|
+
)
|
|
465
|
+
pulseLayer.path = UIBezierPath(ovalIn: rect).cgPath
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
func pulse() {
|
|
469
|
+
let scaleAnim = CABasicAnimation(keyPath: "transform.scale")
|
|
470
|
+
scaleAnim.fromValue = 1.0
|
|
471
|
+
scaleAnim.toValue = 1.5
|
|
472
|
+
|
|
473
|
+
let opacityAnim = CABasicAnimation(keyPath: "opacity")
|
|
474
|
+
opacityAnim.fromValue = 1.0
|
|
475
|
+
opacityAnim.toValue = 0.0
|
|
476
|
+
|
|
477
|
+
let group = CAAnimationGroup()
|
|
478
|
+
group.animations = [scaleAnim, opacityAnim]
|
|
479
|
+
group.duration = 0.6
|
|
480
|
+
group.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
|
481
|
+
|
|
482
|
+
pulseLayer.add(group, forKey: "pulse")
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Reading CA Animation Completion in SwiftUI
|
|
488
|
+
|
|
489
|
+
Use `CAAnimationDelegate` on the Coordinator to report animation completion back to SwiftUI:
|
|
490
|
+
|
|
491
|
+
```swift
|
|
492
|
+
struct AnimatedBadge: UIViewRepresentable {
|
|
493
|
+
@Binding var isAnimationComplete: Bool
|
|
494
|
+
|
|
495
|
+
func makeCoordinator() -> Coordinator { Coordinator(self) }
|
|
496
|
+
|
|
497
|
+
func makeUIView(context: Context) -> UIView {
|
|
498
|
+
let view = UIView()
|
|
499
|
+
let badge = CAShapeLayer()
|
|
500
|
+
badge.path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 40, height: 40)).cgPath
|
|
501
|
+
badge.fillColor = UIColor.systemRed.cgColor
|
|
502
|
+
badge.name = "badge"
|
|
503
|
+
view.layer.addSublayer(badge)
|
|
504
|
+
return view
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
func updateUIView(_ uiView: UIView, context: Context) {}
|
|
508
|
+
|
|
509
|
+
func animateIn(_ uiView: UIView) {
|
|
510
|
+
guard let badge = uiView.layer.sublayers?.first(where: { $0.name == "badge" }) else { return }
|
|
511
|
+
|
|
512
|
+
let spring = CASpringAnimation(perceptualDuration: 0.5, bounce: 0.3)
|
|
513
|
+
spring.keyPath = "transform.scale"
|
|
514
|
+
spring.fromValue = 0.0
|
|
515
|
+
spring.toValue = 1.0
|
|
516
|
+
spring.delegate = uiView.next as? CAAnimationDelegate
|
|
517
|
+
|
|
518
|
+
badge.add(spring, forKey: "appear")
|
|
519
|
+
badge.transform = CATransform3DIdentity
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
final class Coordinator: NSObject, CAAnimationDelegate {
|
|
523
|
+
var parent: AnimatedBadge
|
|
524
|
+
|
|
525
|
+
init(_ parent: AnimatedBadge) { self.parent = parent }
|
|
526
|
+
|
|
527
|
+
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
|
|
528
|
+
if flag {
|
|
529
|
+
parent.isAnimationComplete = true
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
## Performance Considerations
|
|
537
|
+
|
|
538
|
+
### CA vs. SwiftUI Animation Performance
|
|
539
|
+
|
|
540
|
+
| Aspect | SwiftUI Animation | Core Animation |
|
|
541
|
+
|--------|------------------|----------------|
|
|
542
|
+
| **Rendering** | View diffing + render tree | Direct layer manipulation |
|
|
543
|
+
| **Thread** | Main thread for state, render server for compositing | Same -- render server composites |
|
|
544
|
+
| **Overhead** | SwiftUI body re-evaluation per frame (for animatable) | No body re-evaluation |
|
|
545
|
+
| **Best for** | Standard UI transitions | Particle effects, wave animations, complex paths |
|
|
546
|
+
|
|
547
|
+
### Guidelines
|
|
548
|
+
|
|
549
|
+
- **Avoid mixing CA animations and SwiftUI animations on the same property.** They use separate animation systems and will conflict.
|
|
550
|
+
- **Use `CADisplayLink` sparingly.** A running display link prevents the CPU from sleeping. Always invalidate when not needed.
|
|
551
|
+
- **Prefer `CAShapeLayer` for path-based animations** over redrawing in `draw(_:)`. Shape layers are GPU-accelerated.
|
|
552
|
+
- **Set `shouldRasterize = true`** on complex static sublayer trees to cache them as bitmaps, but disable it during animation (rasterization prevents smooth per-frame updates).
|
|
553
|
+
- **Match CA spring parameters to SwiftUI springs** using the `perceptualDuration:bounce:` initializer so animations feel consistent across the bridge boundary.
|