commit 169d0e78f11c809764421878723a3b5380e522de
parent e542c7f27df71ce7393f12739caf6f5b050a7563
Author: Michael Camilleri <[email protected]>
Date: Mon, 23 Mar 2026 16:04:16 +0900
Fix pull-to-create animation
A previous commit attempted to make the pull-to-create animation
smoother by fixing the structure of the view hierarchy. After using the
app on my phone, it became evident that this new structure was broken.
When submitting a new item from a draft row located at the top of the
list, the new list would jump down. This commit restructures the view
hierarchy in a way that eliminates this bug.
Co-Authored-By: Claude 4.6 Opus <[email protected]>
Diffstat:
1 file changed, 17 insertions(+), 25 deletions(-)
diff --git a/ListlessiOS/Views/TaskListView.swift b/ListlessiOS/Views/TaskListView.swift
@@ -239,35 +239,23 @@ struct TaskListView: View, TaskListViewProtocol {
/// The phantom's UITextView is created while the indicator is visible
/// (during the pull), so it's ready when the user releases.
@ViewBuilder var pullToCreateIndicatorRow: some View {
- let showPhantom = isPrependDraftVisible
let pullOffset = pState.pullToCreate.pullOffset
let indicatorHeight = PullToCreateIndicator.indicatorHeight
- ZStack(alignment: .topLeading) {
- PullToCreateIndicator(
- pullOffset: max(
- 0,
- pState.pullToCreate.indicatorDisplayOffset(
- threshold: pullCreateThreshold
- ) - vStackSpacing
- ),
- threshold: max(0, pullCreateThreshold - vStackSpacing),
- hasRowsBelow: false
- )
- .padding(.bottom, -indicatorHeight)
- .opacity(showPhantom ? 0 : 1)
-
- draftPrependRow
- .frame(height: showPhantom ? nil : 0)
- .opacity(showPhantom ? 1 : 0)
- // Instant swap — no animation on height or opacity.
- .animation(nil, value: showPhantom)
- }
+ PullToCreateIndicator(
+ pullOffset: max(
+ 0,
+ pState.pullToCreate.indicatorDisplayOffset(
+ threshold: pullCreateThreshold
+ ) - vStackSpacing
+ ),
+ threshold: max(0, pullCreateThreshold - vStackSpacing),
+ hasRowsBelow: false
+ )
+ .padding(.bottom, -indicatorHeight)
+ .opacity(isPrependDraftVisible ? 0 : 1)
.offset(
- y: showPhantom
- ? pState.draftSettleOffset
- : vStackSpacing - min(pullOffset, indicatorHeight + vStackSpacing)
+ y: vStackSpacing - min(pullOffset, indicatorHeight + vStackSpacing)
)
- .animation(nil, value: showPhantom)
}
/// The draft row content styled to match a task row. Controlled by the
@@ -477,6 +465,10 @@ struct TaskListView: View, TaskListViewProtocol {
Color.clear.frame(height: pState.headerHeight)
pullToCreateIndicatorRow
}
+ if isPrependDraftVisible {
+ draftPrependRow
+ .offset(y: pState.draftSettleOffset)
+ }
taskRows
}
.offset(