← All repositories

listless

A simple list app for Apple platforms

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


commit 1223f5a5baf3b80cf48328dd8a8b1eef30f70361
parent c21a1e4aeb11ec20f2d2316044b4cee4cbf1a2fe
Author: Michael Camilleri <[email protected]>
Date:   Thu, 19 Mar 2026 13:41:23 +0900

Fix further issues on iOS 26

The `.onTapGesture` used previously for the tap-to-create gesture does
not work on iOS 26. This commit uses a hacky alternative that detects
all taps on the ScrollView and then check if the tap is occurring
'below' the rows in the list.

Similarly, `@FetchRequest` previously triggered an update refresh when a
property (like completion) on a result was toggled. This no longer
occurs and so instead a counter is incremented to trigger the refresh.

Co-Authored-By: Claude 4.6 Opus <[email protected]>

Diffstat

MListlessiOS/Views/TaskListView.swift | 24++++++++++++++++++------

1 file changed, 18 insertions(+), 6 deletions(-)

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

@@ -15,6 +15,8 @@ struct TaskListView: View, TaskListViewProtocol {
         var isScrolling: Bool = false
         var draftPlacement: DraftTaskPlacement?
         var draftTitle: String = ""
+        var contentBottomY: CGFloat = 0
+        var fetchWorkaround: Int = 0
     }
 
     @AppStorage("headingText") var headingText = "Items"
@@ -406,6 +408,7 @@ struct TaskListView: View, TaskListViewProtocol {
     }
 
     @ViewBuilder private var taskRows: some View {
+        let _ = iState.fetchWorkaround
         ForEach(Array(displayActiveTasks.enumerated()), id: \.element.id) { index, task in
             let taskID = task.id
             TaskRowView(
@@ -418,7 +421,7 @@ struct TaskListView: View, TaskListViewProtocol {
                 isScrolling: iState.isScrolling,
                 isLastActiveTask: index == displayActiveTasks.count - 1,
                 focusedField: $focusedFieldBinding,
-                onToggle: { toggleCompletion($0) },
+                onToggle: { toggleCompletion($0); withAnimation { iState.fetchWorkaround &+= 1 } },
                 onTitleChange: { updateTitle($0, $1) },
                 onDelete: { deleteTaskWithUndo($0) },
                 onSelect: { selectTask($0) },
@@ -462,7 +465,7 @@ struct TaskListView: View, TaskListViewProtocol {
                 isSelected: fState.selectedTaskID == taskID,
                 isScrolling: iState.isScrolling,
                 focusedField: $focusedFieldBinding,
-                onToggle: { toggleCompletion($0) },
+                onToggle: { toggleCompletion($0); withAnimation { iState.fetchWorkaround &+= 1 } },
                 onTitleChange: { updateTitle($0, $1) },
                 onDelete: { deleteTaskWithUndo($0) },
                 onSelect: { selectTask($0) }
@@ -475,10 +478,12 @@ struct TaskListView: View, TaskListViewProtocol {
 
     var body: some View {
         taskScrollView
-            .contentShape(Rectangle())
-            .onTapGesture {
-                handleBackgroundTap()
-            }
+            .simultaneousGesture(
+                SpatialTapGesture(coordinateSpace: .global).onEnded { value in
+                    guard value.location.y > iState.contentBottomY else { return }
+                    handleBackgroundTap()
+                }
+            )
             .accessibilityIdentifier("task-list-scrollview")
             .background {
                 let isEditing = if case .task = focusedFieldBinding { true } else { false }
@@ -559,6 +564,11 @@ struct TaskListView: View, TaskListViewProtocol {
                     .offset(y: -pullToCreateRowOverlap)
             }
             .frame(maxWidth: .infinity, alignment: .topLeading)
+            .onGeometryChange(for: CGFloat.self) {
+                $0.frame(in: .global).maxY
+            } action: {
+                iState.contentBottomY = $0
+            }
             .padding(.trailing, 16)
             .padding(.vertical, 12)
             .offset(y: -iState.pullToCreate.pullOffset)
@@ -641,3 +651,5 @@ struct TaskListView: View, TaskListViewProtocol {
         )
     }
 }
+
+