- This sample app attempts to replicate the daily timeline of iOS Calendar app - in SwiftUI.
- Using SwiftUI, I was able to create a daily timeline that scrolls and may have an event placed on top of it. This event can be moved around and stretched/contracted, as in the iOS Calendar app. Preference keys ensure that scrolling the timeline also moves the event, creating the sense that the event is pinned to the timeline.
- I am currently stuck on the issue of auto-scrolling when we are moving or stretching an event beyond the currently visible bounds of the timeline. The timeline should automatically scroll up/down. However, the current implementation (which uses scrollTo: method) is very choppy and unreliable. The logic of this implementation is described below.
- One idea that I wanted to test is to ditch animations and scrollTo, and instead to (somehow) set the calendar timeline position using position or offset modifiers. I have not played around with this yet.
- NOTE: Currently, we only implemented the logic for auto-scrolling up while the event is being dragged. Once the problem is resolved, the plan is to implement the logic also for auto-scrolling down, as well as for stretching/contracting the event.
- Drag your finger to scroll the calendar timeline down by a few rows.
- Long press on the event to make it draggable
- Try to drag the event up, to the very first rows (which are now beyond the edge of the screen, as per step (1)
- The timeline will begin to auto-scroll, but this will be very choppy and unreliable.
- CalendarView holds variables triggerAutoScroll(UUID), draggingInAutoScrollArea(Bool) and autoScrollOffset(CGFloat)
- DraggableEvent changes draggingInAutoScrollArea when we enter the "legitimate" area (top/bottom area of the calendar, where we need the timeline to automatically start scrolling up/down), and fires triggerAutoScroll. When the user stops dragging, or the drag position leaves the legitimate area, draggingInAutoScrollArea is switched off, and auto-scroll ends.
- CalendarView detects onChange of triggerAutoScroll and initiates the first auto-scroll. It also calculates autoScrollOffset as a difference between current offset and the row to which we're auto-scrolling (using scrollTo: method).
- DraggableEvent aligns its position to whatever calculation and detracts autoScrollOffset. It then resets it to 0.
- As normalisedOffset aligns with the appropriate row, triggerAutoScroll is updated again. This happens if offset is aligned with any row && draggingInAutoScrollArea == true.
- CalendarView detects onChange of triggerAutoScroll and initiates another auto-scroll.
See comments in source code for more detail
State triggerAutoScroll(UUID)
State draggingInAutoScrollArea(Bool)
State autoScrollOffset(CGFloat) = 0
...
onChange of triggerAutoScroll (we should auto-scroll)
autoScrollOffset = abs(normalisedOffset - rowToScrollTo)
execute scroll using scrollTo method
offset preference change
if draggingInAutoScrollArea == true && offset is at the row { (animation from 1st loop of auto-scroll is finished and we need another)
triggerAutoScroll = UUID()
See comments in source code for more detail
Binding draggingInAutoScrollArea(Bool)
Binding triggerAutoScroll(UUID)
Binding autoScrollOffset(CGFloat)
...
onChanged (with every gesture update)
if in legitimate area && draggingInAutoScrollArea == false (user moved into legitimate area for the first time)
draggingInAutoScrollArea = true
triggerAutoScroll = UUID()
else (user moved outside of legitimate area)
draggingInAutoScrollArea = false
position = whatever position - autoScrollOffset
autoScrollOffset = 0
onEnded (gesture ended)
draggingInAutoScrollArea = false