← All repositories

listless

A simple list app for Apple platforms

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


commit 13bc7a756f8a0c1a343dc6e971ef3b0423690b84
parent 618ffff00f8c46474d1e3f5e486b538c88c304eb
Author: Michael Camilleri <[email protected]>
Date:   Fri, 27 Feb 2026 14:08:22 +0900

Fix focused text field being in view

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

Diffstat

MListlessiOS/Extensions/TaskListView+PullGestures.swift | 5++++-
MListlessiOS/Views/TaskListView.swift | 50+++++++++++++++++++++++++++++++++++---------------

2 files changed, 39 insertions(+), 16 deletions(-)

diff --git a/ListlessiOS/Extensions/TaskListView+PullGestures.swift b/ListlessiOS/Extensions/TaskListView+PullGestures.swift

@@ -77,9 +77,12 @@ private struct PullCreationGestureModifier: ViewModifier {
                 pullToCreate.updatePullDistance(pullDistance)
             }
             .onScrollGeometryChange(for: CGFloat.self) { geo in
+                // Subtract the 20pt bottom content margin (set on ScrollView in TaskListView)
+                // so it doesn't create a dead zone before overscroll registers.
+                let adjustedBottomInset = geo.contentInsets.bottom - 20
                 let maxOffset = max(
                     -geo.contentInsets.top,
-                    geo.contentSize.height - geo.bounds.size.height + geo.contentInsets.bottom
+                    geo.contentSize.height - geo.bounds.size.height + adjustedBottomInset
                 )
                 return max(0, geo.contentOffset.y - maxOffset)
             } action: { _, pullDistance in

diff --git a/ListlessiOS/Views/TaskListView.swift b/ListlessiOS/Views/TaskListView.swift

@@ -145,21 +145,6 @@ struct TaskListView: View, TaskListViewProtocol {
                 }
                 fState.focusedField = focusedFieldBinding
             }
-            .onChange(of: focusedFieldBinding) { oldValue, newValue in
-                fState.focusedField = newValue
-                handleFocusChange(from: oldValue, to: newValue)
-
-                if newValue == nil {
-                    if let pending = pendingFocus {
-                        focusedFieldBinding = pending
-                        fState.focusedField = pending
-                        pendingFocus = nil
-                    } else {
-                        focusedFieldBinding = .scrollView
-                        fState.focusedField = .scrollView
-                    }
-                }
-            }
             .onChange(of: undoManager, initial: true) { _, newValue in
                 managedObjectContext.undoManager = newValue
             }
@@ -200,6 +185,7 @@ struct TaskListView: View, TaskListViewProtocol {
 
     private var taskScrollView: some View {
         ScrollView {
+          ScrollViewReader { scrollProxy in
             VStack(alignment: .leading, spacing: vStackSpacing) {
                 navigationHeader
                 pullToCreateIndicatorRow
@@ -239,6 +225,7 @@ struct TaskListView: View, TaskListViewProtocol {
                     } action: { frame in
                         rowFrames[taskID] = frame
                     }
+                    .id(taskID)
                 }
 
                 ForEach(completedTasks) { task in
@@ -256,15 +243,48 @@ struct TaskListView: View, TaskListViewProtocol {
                     )
                     .opacity(isBeingCleared ? 0 : 1)
                     .offset(y: isBeingCleared ? 40 : 0)
+                    .id(taskID)
                 }
             }
             .frame(maxWidth: .infinity, alignment: .topLeading)
             .padding(.trailing, 16)
             .padding(.vertical, 12)
             .offset(y: -pullToCreate.pullOffset)
+            .onChange(of: focusedFieldBinding) { oldValue, newValue in
+                fState.focusedField = newValue
+                handleFocusChange(from: oldValue, to: newValue)
+
+                if newValue == nil {
+                    if let pending = pendingFocus {
+                        focusedFieldBinding = pending
+                        fState.focusedField = pending
+                        pendingFocus = nil
+                    } else {
+                        focusedFieldBinding = .scrollView
+                        fState.focusedField = .scrollView
+                    }
+                }
+
+                if case .task(let id) = (newValue ?? fState.focusedField),
+                    draggedTaskID == nil
+                {
+                    withAnimation {
+                        scrollProxy.scrollTo(id)
+                    }
+                }
+            }
+            .onChange(of: fState.selectedTaskID) { _, newID in
+                if let newID, draggedTaskID == nil {
+                    withAnimation {
+                        scrollProxy.scrollTo(newID)
+                    }
+                }
+            }
+          }
         }
         .scrollDisabled(draggedTaskID != nil)
         .scrollBounceBehavior(.always)
+        .contentMargins(.bottom, 20)
         .background {
             Color.outerBackground.ignoresSafeArea()
         }