motioneyes-animation-debug
MotionEyes Animation Debug
Overview
Use MotionEyes as temporary observability for SwiftUI animation debugging. Instrument targeted values and geometry, capture time-series logs, compare observed motion against expected motion, apply fixes, re-validate, and clean up all agent-added tracing.
Use This Skill When
- An animation moves in the wrong direction, starts too late, or ends too early.
- Opacity, offset, or position changes do not match expected timing.
- Two elements drift or desynchronize during a transition.
- ScrollView offsets jump, drift, or fail to restore.
- You need evidence from runtime motion values rather than visual guesses.
Do Not Use If
- The problem is unrelated to SwiftUI animation or scroll behavior.
- You cannot reproduce the behavior in a simulator or device.
Agent Behavior Contract
Follow these rules for every run:
- Confirm the complaint and expected behavior in measurable terms before instrumentation.
- Add only the minimum traces needed to test the complaint.
- Prefer XcodeBuildMCP for log capture; fall back to CLI log streaming if MCP is unavailable.
- Use semantic trace names that map directly to the user intent.
- Preserve all pre-existing MotionEyes instrumentation.
- Remove only agent-added MotionEyes imports, modifiers, and metrics after validation.
- Re-run after fixes and verify against logs, not assumptions.
Workflow
Follow this exact order:
- Confirm the complaint and expected behavior in measurable terms.
- Locate the target view and the state values that drive the animation.
- Ensure MotionEyes availability; if missing, auto-integrate the MotionEyes package into the app target before continuing.
- Add temporary
.motionTrace(...)instrumentation withTrace.value,Trace.geometry, and (for scroll issues)Trace.scrollGeometrymetrics named after user intent. - Run the app and reproduce the issue.
- Capture logs with XcodeBuildMCP first; fall back to CLI log streaming if MCP is unavailable.
- Analyze how values evolve over time versus expected behavior.
- Implement a fix, rerun, and verify the motion now matches intent.
- Remove only agent-added MotionEyes imports, modifiers, and trace metrics from this run; never remove user-authored pre-existing MotionEyes code.
Intent Mapping (Screen vs Layout vs Local)
Choose the geometry mode that matches what the user actually cares about.
- User-visible/on-screen motion (phrases like "did it move?", "what the user sees", "slide on screen"):
Trace.geometry(..., space: .screen, source: .presentation)
- Layout relationships/spacing (phrases like "aligned", "spacing", "relative to container", "stays in the same stack position"):
Trace.geometry(..., space: .swiftUI(.global), source: .layout)
- Local container relationships (phrases like "within this card", "inside this stack", "relative to parent bounds"):
Trace.geometry(..., space: .swiftUI(.local), source: .layout)or a named coordinate space
If you are unsure, start with on-screen motion and add layout geometry only if you need relationships.
Quick Decision Tree
- If you need to observe a single value changing over time, start with
Trace.value. - If the user cares about visible on-screen motion, start with
Trace.geometryusingspace: .screen, source: .presentation. - If the issue involves layout relationships or spacing, add
Trace.geometrywithspace: .swiftUI(.global), source: .layout(or.localfor local container relationships). - If the first geometry trace is flat but the user sees motion, add the complementary geometry mode (layout vs presentation) and compare.
- If the bug involves scrolling or restoration, use
Trace.scrollGeometryon theScrollViewcontainer.
Triage-First Playbook
- Fade or visibility mismatch:
Trace.value("opacity", opacity). - Offset or translation issues:
Trace.value("offset", CGPoint(x: offset.width, y: offset.height)). - Missing or disputed on-screen motion:
Trace.geometryin.screen + .presentation(add layout if needed). - Position drift between elements:
Trace.geometryfor both views in the same space that matches intent. - Timing mismatch: trace the driving state with
Trace.valueplus one geometry metric. - Scroll jumps or restoration drift:
Trace.scrollGeometrywithcontentOffsetandvisibleRectmetrics. - Unexpected motion when something should remain still:
Trace.geometryin.screen + .presentation.
Instrumentation Rules
Add instrumentation only to the minimum set of views needed to test the complaint.
- Use stable, semantic trace names that match the user complaint.
- Set the values to the same name as the property, so it is easier to identify.
- Default to on-screen motion (
.screen + .presentation) when the user is describing what they see. - Use layout geometry when the issue is about spacing, alignment, or container/sibling relationships.
- Use scroll geometry tracing when the bug involves
ScrollViewoffset, visible region, content size, insets, or restoration behavior. - Place
Trace.scrollGeometryon theScrollViewcontainer or an immediate descendant bound to the same scroll context. - If a geometry trace is flat but the user reports motion, add the complementary geometry mode and compare.
- Scroll caveat: layout frames can stay stable while scroll geometry or presentation geometry changes.
Choose geometry mode based on intent:
- Layout relationship in SwiftUI coordinates:
space: .swiftUI(.global), source: .layout - Local container relationship:
space: .swiftUI(.local), source: .layout(or a named coordinate space) - Window-relative layout motion:
space: .window, source: .layout - Physical screen-visible motion:
space: .screen, source: .presentation
Example Template
import MotionEyes
import SwiftUI
struct CardMotionExample: View {
@State private var opacity = 1.0
@State private var offset = CGSize.zero
var body: some View {
RoundedRectangle(cornerRadius: 16)
.fill(.orange)
.frame(width: 180, height: 120)
.opacity(opacity)
.offset(offset)
.motionTrace("Card Motion", fps: 30) {
Trace.value("opacity", opacity)
Trace.value("offset", CGPoint(x: offset.width, y: offset.height))
Trace.geometry(
"cardFrame",
properties: [.minX, .minY, .width, .height],
space: .screen,
source: .presentation
)
}
.onTapGesture {
withAnimation(.easeInOut(duration: 0.6)) {
opacity = opacity == 1 ? 0.4 : 1
offset = offset == .zero ? CGSize(width: 0, height: 36) : .zero
}
}
}
}
Scroll-Focused Template
ScrollView {
content
}
.motionTrace("Chat Scroll", fps: 30) {
Trace.scrollGeometry(
"scrollMetrics",
properties: [.contentOffsetY, .visibleRectMinY, .visibleRectHeight]
)
}
Log Capture
Prefer XcodeBuildMCP:
- Call
mcp__XcodeBuildMCP__session_show_defaults. - Set missing defaults with
mcp__XcodeBuildMCP__session_set_defaults. - Build and run with
mcp__XcodeBuildMCP__build_run_simif needed. - Start capture with
mcp__XcodeBuildMCP__start_sim_log_capusingcaptureConsole: trueandsubsystemFilter: "MotionEyes". - Reproduce the animation.
- Stop capture with
mcp__XcodeBuildMCP__stop_sim_log_capand inspect returned logs.
Fallback CLI if MCP is unavailable:
xcrun simctl spawn booted log stream \
--style compact \
--level debug \
--predicate 'subsystem == "MotionEyes"'
MotionEyes Log Analysis
Use these signatures:
- Value samples:
[MotionEyes][View][Metric] key=value ... - Change burst start:
[MotionEyes][View][Metric] -- Start timestamp -- - Change burst end:
[MotionEyes][View][Metric] -- End timestamp --
Analyze:
- Direction: sign and trend of sampled values.
- Timing: time delta from intent trigger to first
Startand toEnd. - Shape: monotonic change, overshoot, reversals, oscillation, flat segments.
- Relationship: compare two traces over the same time window when behavior is relative.
Do not force fixed thresholds globally; evaluate against the user’s stated expectation.
Verification Checklist
- Trace names map directly to the user complaint.
- Motion
StartandEndmarkers align with the expected interaction timeline. - Direction, timing, and shape match the expected motion.
- Relative motion metrics stay within expected deltas.
- The fix is verified by a second log capture.
- Agent-added instrumentation is removed after validation.
- The project still builds after cleanup.
Cleanup Rules
At the end of every run:
- Remove all MotionEyes instrumentation introduced by the agent during this debugging run.
- Keep all MotionEyes instrumentation that already existed before the run.
- Remove agent-added
import MotionEyesonly if it was added solely for temporary tracing and is no longer needed. - Confirm code compiles after cleanup.
Scenarios to Support
- Fade timing bug: trace
opacityand verify fade begins and ends when expected. - Wrong direction bug: trace Y-related value and confirm sign and trend match expected motion.
- Relative motion bug: trace two objects and verify their positional relationship over time.
- Scroll jump or restoration bug: trace
Trace.scrollGeometryon theScrollViewand verifycontentOffsetandvisibleRectprogression through navigation and return paths. - No motion desired: confirm a view remains stable during transitions.
- Existing instrumentation safety: preserve user-authored MotionEyes traces.
- MCP unavailable: use CLI log stream and continue analysis.
- Missing package: auto-integrate MotionEyes, then execute normal workflow.
Reference
Load references/motioneyes-observability-patterns.md when choosing metrics or interpreting trace behavior.