Files
SportstimeAPI/.planning/research/FEATURES.md
Trey t 6f09c0abcb docs: complete project research for itinerary editor
Files:
- STACK.md - UITableView drag-drop APIs, SwiftUI bridging patterns
- FEATURES.md - Table stakes UX (lift, insertion line, haptics), polish features
- ARCHITECTURE.md - 5-layer semantic positioning architecture
- PITFALLS.md - Critical pitfalls (row vs semantic, travel as structural)
- SUMMARY.md - Executive synthesis with roadmap implications

Key findings:
- Stack: UITableView + UIHostingConfiguration (existing pattern validated)
- Architecture: Semantic (day, sortOrder) model, not row indices
- Critical pitfall: Row indices are ephemeral; semantic positions are truth

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 13:16:07 -06:00

12 KiB

Features Research: Drag-Drop Editor UX

Domain: Drag-and-drop itinerary editor for iOS sports travel app Researched: 2026-01-18 Confidence: HIGH (multiple authoritative sources cross-verified)

Executive Summary

Polished drag-drop requires deliberate visual feedback at every state transition. The difference between "feels broken" and "feels delightful" comes down to: lift animation, predictable insertion indicators, smooth reshuffling, and magnetic snap-to-place. Your constraints (fixed day headers, fixed games, movable travel/custom items) add complexity but are achievable with proper drop zone logic.


Table Stakes

Features users expect. Missing any of these makes the editor feel broken.

Feature Why Expected Complexity Implementation Notes
Lift animation on grab Users expect physical metaphor - picking up an object Low Elevation (shadow), scale 1.02-1.05x, slight z-offset
Ghost/placeholder at origin Shows where item came from, reduces anxiety Low Semi-transparent copy or outlined placeholder in original position
Insertion indicator line Must show exactly where item will drop Medium Horizontal line with small terminal bleeds, appears between items
Items move out of the way Preview of final state while dragging Medium ~100ms animation, triggered when dragged item center overlaps edge
Magnetic snap on drop Satisfying completion, confirms action worked Low 100ms ease-out animation to final position
Clear invalid drop feedback Don't leave user guessing why drop failed Low Item animates back to origin if dropped in invalid zone
Touch hold delay (300-500ms) Distinguish tap from drag intent Low iOS standard; prevents accidental drags
Haptic on grab Tactile confirmation drag started Low UIImpactFeedbackGenerator.light on pickup
Haptic on drop Tactile confirmation action completed Low UIImpactFeedbackGenerator.medium on successful drop
Scroll when dragging to edge Lists longer than viewport need auto-scroll Medium Scroll speed increases closer to edge, ~40px threshold

Insertion Indicator Details

The insertion line is critical. Best practices:

  • Appears between items (in the gap), not on top
  • Has small terminal bleeds (~4px) extending past item edges
  • Triggered when center of dragged item crosses edge of potential neighbor
  • Color should contrast clearly (system accent or distinct color)

Animation Timing

Event Duration Easing
Lift (pickup) 150ms ease-out
Items shuffling 100ms ease-out
Snap to place (drop) 100ms ease-out
Return to origin (cancel) 200ms ease-in-out

Nice-to-Have

Polish features that delight but aren't expected.

Feature Value Complexity Notes
Slight tilt on drag (2-3 degrees) Trello's signature polish; makes interaction feel playful Low Rotate3D effect, matches brand personality
Progressive drop zone highlighting Visual intensifies as item approaches valid zone Medium Background color change, border enhancement
Multi-item drag with count badge Power users moving multiple items at once High Not needed for v1; itinerary items are usually moved one at a time
Keyboard reordering (a11y) Up/Down arrows via rotor actions Medium Important for accessibility; add accessibilityActions
Undo after drop Recover from mistakes Medium Toast with "Undo" button, ~5 second timeout
Drag handle icon Visual affordance for draggability Low 6-dot grip icon (Notion-style) or horizontal lines
Cancel drag with escape/shake Quick abort Low Shake-to-cancel on iOS; return to origin
Drop zone "ready" state Zone visually activates before item enters Low Subtle background shift when drag starts

Tilt Animation (Trello-style)

The 2-3 degree tilt on dragged items is considered "gold standard" polish:

  • Adds personality without being distracting
  • Reinforces physical metaphor (picking up a card)
  • Should match your app's design language (may be too playful for some apps)

Overkill

Skip these - high complexity, low value for an itinerary editor.

Feature Why Skip What to Do Instead
Drag between sections/screens Your items live within days; cross-day moves are rare Allow within same list only, or use "Move to..." action menu
Nested drag-drop Games within days is hierarchy enough Keep flat list per day section
Free-form canvas positioning Not applicable to linear itinerary Stick to list reordering
Real-time collaborative drag Massive sync complexity Single-user editing
Drag-to-resize Items don't have variable size Fixed item heights
Custom drag preview images Native preview is sufficient Use default lifted appearance
Physics-based spring animations Overkill for list reordering Simple ease-out is fine

Interactions to Support

Specific drag scenarios for your itinerary context.

Scenario 1: Move Custom Item Within Same Day

User intent: Reorder "Dinner at Lou Malnati's" from after to before the Cubs game

Expected behavior:

  1. Long-press on custom item (300ms) - haptic feedback
  2. Item lifts (shadow + scale), ghost remains at origin
  3. Drag within day section - insertion line appears between valid positions
  4. Games and travel segments shuffle with 100ms animation
  5. Drop - item snaps into place, haptic confirms

Constraints:

  • Custom item can move anywhere within the day
  • Cannot move before/after day header
  • Cannot replace or overlay a game (games are fixed)

Scenario 2: Move Custom Item to Different Day

User intent: Move hotel check-in from Day 2 to Day 1

Expected behavior:

  1. Long-press and lift
  2. Drag toward Day 1 section
  3. Auto-scroll if Day 1 is off-screen
  4. Insertion line appears at valid positions in Day 1
  5. Day 2 collapses to show item removed; Day 1 expands
  6. Drop - item now in Day 1

Constraints:

  • Can cross day boundaries
  • Still cannot land on games

Scenario 3: Move Travel Segment (Constrained)

User intent: Move "Drive: Chicago to Milwaukee" earlier in the day

Expected behavior:

  1. Long-press on travel segment
  2. Item lifts (possibly with different visual treatment since it's constrained)
  3. Insertion line only appears at valid positions (before/after games it connects)
  4. Invalid positions show no insertion line (or dimmed indicator)
  5. If dropped at invalid position, item animates back to origin

Constraints:

  • Travel segments connect stadiums/locations
  • Can only move within logical route order
  • Must validate position before showing insertion indicator

Scenario 4: Attempt to Move Fixed Item (Game)

User intent: User tries to drag a game (not allowed)

Expected behavior:

  1. Long-press on game item
  2. No lift animation - item doesn't respond as draggable
  3. Optionally: subtle shake or tooltip "Games cannot be reordered"
  4. User understands this item is fixed

Visual differentiation:

  • Fixed items should NOT have drag handles
  • Could have different visual treatment (no grip icon, different background)

Scenario 5: Drag to Invalid Zone

User intent: User drags custom item but releases over a game

Expected behavior:

  1. Item is being dragged
  2. Hovers over game - no insertion line appears (invalid)
  3. User releases
  4. Item animates back to origin (~200ms)
  5. Optional: brief error state or haptic warning

Visual States Summary

Element State Visual Treatment
Resting (draggable) Normal appearance, optional drag handle icon on hover/focus
Resting (fixed) Normal, but NO drag handle; visually distinct
Lifted/grabbed Elevated (shadow), slight scale up (1.02-1.05), optional tilt
Ghost at origin Semi-transparent (30-50% opacity) or outlined placeholder
Insertion line Accent-colored horizontal line, ~2px height, bleeds past edges
Invalid drop zone No insertion line; item over zone dims or shows warning
Drop zone ready Subtle background color shift when any drag starts
Dropped/success Snaps to place, haptic feedback, ghost disappears
Cancelled/error Returns to origin with animation, optional warning haptic

Accessibility Requirements

Requirement Implementation Priority
VoiceOver reordering accessibilityActions with "Move Up" / "Move Down" High
Rotor integration Actions appear in VoiceOver rotor High
Focus management Focus follows moved item after reorder Medium
Live region announcements Announce position change ("Item moved to position 3") Medium
Fallback buttons Optional up/down arrows as visual alternative Low (nice to have)

SwiftUI example for accessibility:

.accessibilityAction(named: "Move Up") { moveItemUp(item) }
.accessibilityAction(named: "Move Down") { moveItemDown(item) }

Mobile-Specific Considerations

Concern Solution
Fat finger problem Minimum 44x44pt touch targets; drag handles at least 44pt wide
Scroll vs. drag conflict Long-press delay (300-500ms) distinguishes intent
Viewport limitations Auto-scroll at edges (40px threshold), speed increases near edge
One-handed use Consider "Move to..." button as alternative to long-distance drags
Accidental drops Generous drop zones; magnetic snap; undo option

Anti-Patterns to Avoid

Anti-Pattern Why Bad Do Instead
Edge-to-edge shuffle trigger Feels "twitchy", items move unexpectedly Use center-overlap-edge trigger
Instant reshuffle (no animation) Disorienting, hard to track what moved 100ms animated transitions
No ghost/placeholder User loses context of original position Always show origin indicator
Drag handle too small Frustrating on touch Minimum 44pt, ideally larger
Remove item during drag Anxiety - "where did it go?" Keep ghost visible at origin
Scroll too fast at edges Overshoots, loses control Gradual speed increase
No invalid feedback User thinks interaction is broken Clear visual/haptic for invalid drops

Sources

High Confidence (verified with multiple authoritative sources):

Medium Confidence (single authoritative source):

Low Confidence (community patterns):

  • Various SwiftUI implementation guides (verify APIs against current documentation)
  • Trello UX patterns referenced in multiple articles (de facto standard)