listless

A simple list app for Apple platforms
Log | Files | Refs | README | LICENSE

commit 04939b02c703e7f8d94e4e6ed30951020088d1fe
parent b9c2b35dc7f8aabe4d7bd6c0f02005fe7248f72a
Author: Michael Camilleri <[email protected]>
Date:   Wed, 18 Feb 2026 19:05:38 +0900

Add polish to pull-to-clear animations

Co-Authored-By: Codex GPT 5.3 <[email protected]>

Diffstat:
MListlessiOS/Helpers/TaskRowMetrics.swift | 2+-
MListlessiOS/Views/PullToClear.swift | 32++++++++++++++++++++++++--------
2 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/ListlessiOS/Helpers/TaskRowMetrics.swift b/ListlessiOS/Helpers/TaskRowMetrics.swift @@ -7,5 +7,5 @@ enum TaskRowMetrics { static let contentVerticalPadding: CGFloat = 14 static let contentHorizontalPadding: CGFloat = 16 static let activeLeadingPadding: CGFloat = 24 - static let completedLeadingPadding: CGFloat = 16 + static let completedLeadingPadding: CGFloat = 24 } diff --git a/ListlessiOS/Views/PullToClear.swift b/ListlessiOS/Views/PullToClear.swift @@ -8,17 +8,33 @@ struct PullToClearIndicator: View { private var progress: CGFloat { min(1, pullOffset / pullClearThreshold) } private var isReady: Bool { pullOffset >= pullClearThreshold } + private let textSlideDistance: CGFloat = 22 var body: some View { HStack(spacing: 6) { - Image(systemName: isReady ? "checkmark" : "trash") - .foregroundStyle(.secondary) - .fontWeight(.semibold) - .animation(.easeInOut(duration: 0.15), value: isReady) - Text(isReady ? "Release to clear" : "Clear completed") - .foregroundStyle(.secondary) - .font(.body) - .animation(.easeInOut(duration: 0.15), value: isReady) + ZStack { + Image(systemName: "checkmark") + .offset(y: isReady ? 0 : -textSlideDistance) + Image(systemName: "tray") + .offset(y: isReady ? textSlideDistance : 0) + } + .frame(width: 26, height: textSlideDistance, alignment: .leading) + .clipped() + .foregroundStyle(.secondary) + .font(.system(size: 17)) + .fontWeight(.semibold) + .animation(.easeInOut(duration: 0.15), value: isReady) + ZStack(alignment: .leading) { + Text("Release to clear") + .offset(y: isReady ? 0 : -textSlideDistance) + Text("Clear completed") + .offset(y: isReady ? textSlideDistance : 0) + } + .foregroundStyle(.secondary) + .font(.body) + .frame(height: textSlideDistance, alignment: .topLeading) + .clipped() + .animation(.easeInOut(duration: 0.15), value: isReady) } .frame(maxWidth: .infinity) .frame(height: 56)