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:
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 {
)
}
}
+
+