fix: auto-scroll loads more when near bottom
onAppear doesn't fire during programmatic scrolling (UIScrollView contentOffset changes don't trigger SwiftUI lifecycle). Added onNearBottom callback to ScrollViewDriver — fires when within 500pt of bottom during auto-scroll tick. 3s cooldown prevents rapid-fire. Auto-scroll no longer stops at bottom — idles at maxOffset while loadMore fetches. When new entries arrive, contentSize grows and scrolling resumes automatically. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -40,8 +40,10 @@ struct EntryListView: View {
|
||||
} else {
|
||||
ScrollView {
|
||||
// Auto-scroll engine — zero-size, drives parent UIScrollView
|
||||
ScrollViewDriver(isScrolling: $isAutoScrolling, speed: scrollSpeed)
|
||||
.frame(width: 0, height: 0)
|
||||
ScrollViewDriver(isScrolling: $isAutoScrolling, speed: scrollSpeed) {
|
||||
Task { await vm.loadMore() }
|
||||
}
|
||||
.frame(width: 0, height: 0)
|
||||
|
||||
if isCardView {
|
||||
cardLayout
|
||||
|
||||
@@ -7,6 +7,7 @@ import UIKit
|
||||
struct ScrollViewDriver: UIViewRepresentable {
|
||||
@Binding var isScrolling: Bool
|
||||
let speed: Double // 1.0 = 60pt/sec
|
||||
var onNearBottom: (() -> Void)? = nil
|
||||
|
||||
func makeUIView(context: Context) -> UIView {
|
||||
let view = DriverView()
|
||||
@@ -21,6 +22,7 @@ struct ScrollViewDriver: UIViewRepresentable {
|
||||
let coordinator = context.coordinator
|
||||
coordinator.speed = speed
|
||||
coordinator.isScrollingBinding = $isScrolling
|
||||
coordinator.onNearBottom = onNearBottom
|
||||
|
||||
if isScrolling && coordinator.displayLink == nil {
|
||||
coordinator.startScrolling(in: driver)
|
||||
@@ -54,8 +56,10 @@ struct ScrollViewDriver: UIViewRepresentable {
|
||||
var displayLink: CADisplayLink?
|
||||
var speed: Double = 1.0
|
||||
var isScrollingBinding: Binding<Bool>?
|
||||
var onNearBottom: (() -> Void)?
|
||||
private var originalDelegate: UIScrollViewDelegate?
|
||||
private var delegateInstalled = false
|
||||
private var loadMoreTriggered = false
|
||||
|
||||
func findScrollView(from view: UIView) {
|
||||
var current: UIView? = view.superview
|
||||
@@ -110,9 +114,21 @@ struct ScrollViewDriver: UIViewRepresentable {
|
||||
|
||||
originalDelegate?.scrollViewDidScroll?(sv)
|
||||
|
||||
if newY >= maxOffset - 1 {
|
||||
stopAndNotify()
|
||||
// Trigger load more when within 500pt of bottom
|
||||
let distanceToBottom = maxOffset - newY
|
||||
if distanceToBottom < 500 && !loadMoreTriggered {
|
||||
loadMoreTriggered = true
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.onNearBottom?()
|
||||
// Reset after a delay so it can trigger again for the next page
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
|
||||
self?.loadMoreTriggered = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't stop at bottom — contentSize may grow after loadMore.
|
||||
// The tick keeps running; if no more content, it just idles at maxOffset.
|
||||
}
|
||||
|
||||
private func stopAndNotify() {
|
||||
|
||||
Reference in New Issue
Block a user