diff --git a/ios/Platform/Platform/Features/Reader/Views/EntryListView.swift b/ios/Platform/Platform/Features/Reader/Views/EntryListView.swift index 3e6ff27..cba81bf 100644 --- a/ios/Platform/Platform/Features/Reader/Views/EntryListView.swift +++ b/ios/Platform/Platform/Features/Reader/Views/EntryListView.swift @@ -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 diff --git a/ios/Platform/Platform/Features/Reader/Views/ScrollViewDriver.swift b/ios/Platform/Platform/Features/Reader/Views/ScrollViewDriver.swift index 6ea1102..88cf223 100644 --- a/ios/Platform/Platform/Features/Reader/Views/ScrollViewDriver.swift +++ b/ios/Platform/Platform/Features/Reader/Views/ScrollViewDriver.swift @@ -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? + 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() {