← All repositories

listless

A simple list app for Apple platforms

git clone https://code.inqk.net/listless.git


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)