feat: Phase 1 auto-scroll engine for Reader feed
ENGINE (ScrollViewDriver.swift): - UIViewRepresentable placed inside ScrollView (zero size) - Finds parent UIScrollView via view hierarchy traversal - CADisplayLink at 60fps drives contentOffset.y smoothly - Speed: 1.0x = 60pt/sec, adjustable 0.25x–3.0x in 0.25 steps - User touch detection: intercepts UIScrollViewDelegate scrollViewWillBeginDragging → stops auto-scroll immediately - Stops at bottom (contentOffset >= maxOffset) - Forwards all delegate methods to SwiftUI's original delegate INTEGRATION (EntryListView): - Accepts @Binding isAutoScrolling + scrollSpeed - ScrollViewDriver placed as first child in ScrollView - Auto-scroll stops on: user touch, navigation back (onAppear), filter change, sub-tab change, reaching bottom CONTROLS (ReaderTabView — temporary, Phase 1): - Play/Stop button in toolbar (play.fill / stop.fill) - When playing: [-] speed [+] controls appear inline - Speed shown as "1.00x" with monospacedDigit MARK-AS-READ: - Auto-scroll drives real UIScrollView contentOffset - This moves LazyVStack rows, triggering their GeometryReader onChange callbacks — the existing mark-as-read system fires naturally with no special case or bypass needed Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,7 @@
|
||||
A10045 /* ArticleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10045 /* ArticleView.swift */; };
|
||||
A10046 /* ArticleWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10046 /* ArticleWebView.swift */; };
|
||||
A10047 /* FeedManagementSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10047 /* FeedManagementSheet.swift */; };
|
||||
A10048 /* ScrollViewDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10048 /* ScrollViewDriver.swift */; };
|
||||
F20549752F805F5800AE8DF5 /* ConfettiSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = F20549742F805F5800AE8DF5 /* ConfettiSwiftUI */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@@ -97,6 +98,7 @@
|
||||
B10045 /* ArticleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleView.swift; sourceTree = "<group>"; };
|
||||
B10046 /* ArticleWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleWebView.swift; sourceTree = "<group>"; };
|
||||
B10047 /* FeedManagementSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedManagementSheet.swift; sourceTree = "<group>"; };
|
||||
B10048 /* ScrollViewDriver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewDriver.swift; sourceTree = "<group>"; };
|
||||
C10001 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
D10001 /* Platform.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Platform.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
@@ -336,6 +338,7 @@
|
||||
B10045 /* ArticleView.swift */,
|
||||
B10046 /* ArticleWebView.swift */,
|
||||
B10047 /* FeedManagementSheet.swift */,
|
||||
B10048 /* ScrollViewDriver.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
@@ -454,6 +457,7 @@
|
||||
A10045 /* ArticleView.swift in Sources */,
|
||||
A10046 /* ArticleWebView.swift in Sources */,
|
||||
A10047 /* FeedManagementSheet.swift in Sources */,
|
||||
A10048 /* ScrollViewDriver.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user