Reduce Motion Settings
Relevant source files
The following files were used as context for generating this wiki page:
- Sources/SwiftDitherKit/DitherMotion.swift
- Sources/SwiftDitherKit/SwiftDitherKit.docc/InteractionAndAccessibility.md
- Sources/SwiftDitherKit/DitherRasterView.swift
- Tests/SwiftDitherKitTests/DitherMotionTests.swift
- Sources/SwiftDitherKit/DitherSparkles.swift
- Examples/MotionDemo/MotionDemo/MotionDemoView.swift
Reduce Motion Settings in SwiftDitherKit ensure that chart visualizations remain accessible and usable for users who prefer or require limited screen movement. The system detects the system-wide accessibilityReduceMotion setting to alter how transitions, effects, and idle animations are rendered.
When Reduce Motion is active, complex spatial animations—such as sliding leading reveals or radial scaling—are replaced by simple opacity fades. Additionally, high-frequency visual effects like blinking "sparkles" are stabilized to a steady state to prevent potential triggers or distractions.
System Architecture and Logic
The motion system is governed by a set of decision-making structures that evaluate the current state of the UI against system preferences. The DitherRasterMotionDecision engine determines whether to perform a full animation, a crossfade, or an immediate replacement based on changes in data, selection, or accessibility settings.
Motion Decision Logic
The system evaluates transitions between DitherRasterMotionState instances. If the reduceMotion flag changes between states, the system triggers a "settle" action to immediately align the view to the final state using a shortened duration.
Key Motion Components
| Component | Description |
|---|---|
DitherMotion | Static utility providing specialized Animation curves and duration constants. |
DitherRasterMotionState | A Sendable struct capturing checksums, tokens, and the current reduceMotion preference. |
DitherRasterMotionDecision | An enumeration defining transition types: none, replace, crossfade, replay, and settle. |
DitherRasterTransitionProgress | Tracks the interpolation values (entrance and update) for GPU-accelerated reveals. |
Sources: DitherMotion.swift:7-73, DitherRasterView.swift:15-25
Visual Adjustments in Reduced Motion
When reduceMotion is enabled, the visual output is modified across three primary areas: transitions, masking effects, and idle animations.
1. Transitions and Blurs
Standard transitions in SwiftDitherKit often involve a "transition blur." With Reduce Motion, this blur is completely removed (set to 0) to avoid processing-heavy or visually distracting smearing effects.
- Standard Updates: Use
updateDuration(0.22s). - Reduced Motion Updates: Use
reducedMotionDuration(0.14s). - Blur Calculation:
reduceMotion ? 0 : clamped(progress) * transitionBlur.
Sources: DitherMotion.swift:10-37, DitherMotionTests.swift:16-27
2. Reveal and Scale Effects
Different chart types utilize specific reveal styles. Reduce Motion forces these styles to rely exclusively on opacity rather than spatial movement.
| Reveal Style | Standard Behavior | Reduced Motion Behavior |
|---|---|---|
leading | Horizontal wipe (Cartesian charts) | Opacity fade |
angular | Clockwise wipe (Pie charts) | Opacity fade |
radial | Scaling from 0.96 to 1.0 (Radar charts) | Opacity fade (No scaling) |
Sources: DitherRasterView.swift:155-163, DitherRasterView.swift:293-315, DitherMotion.swift:30-32
3. Idle Sparkle Stabilization
Area and line charts typically display "sparkles" that twinkle based on a sine wave calculation. When motion is reduced, these sparkles stop blinking.
Implementation Details
The DitherAnimatedRaster view synchronizes these settings using the @Environment(\.accessibilityReduceMotion) property wrapper.
// Example of motion synchronization logic
@MainActor
private func synchronizeMotion(to next: DitherRasterMotionState) async {
let decision = DitherRasterMotionDecision.decide(from: lastMotionState, to: next)
lastMotionState = next
switch decision {
case .settle:
previousFrame = nil
progress.update = 1
withAnimation(DitherMotion.reduced) {
progress.entrance = 1
}
// ... other cases
}
}
Sources: DitherRasterView.swift:85-115, MotionDemoView.swift:4-10
Summary of Reduced Motion Logic
The system ensures that an interrupted entrance or a mid-animation toggle of the Reduce Motion setting does not leave replacement content masked out. The settle() function is called to immediately force entrance and update progress to 1, ensuring visual consistency.
Sources: DitherMotion.swift:69-73, DitherMotionTests.swift:57-73, InteractionAndAccessibility.md:38-40