|
8 | 8 |
|
9 | 9 | import SwiftUI |
10 | 10 |
|
11 | | -/// A class that manages programmatic scrolling within a |
12 | | -/// scroll view that uses sticky headers. |
| 11 | +/// This class can be used for programmatic scrolling within |
| 12 | +/// a scroll view. |
13 | 13 | /// |
14 | | -/// `ScrollManager` can be used to scroll to specific |
15 | | -/// parts of a scroll view (e.g. the sticky header or |
16 | | -/// the main content) using a `ScrollViewProxy`. |
| 14 | +/// This class can be used to scroll to any specific part of |
| 15 | +/// a scroll view (e.g. the header or the main content) with |
| 16 | +/// a `ScrollViewProxy`. Simply add a ``ScrollTarget`` ID to |
| 17 | +/// your scroll view's header view and content then call any |
| 18 | +/// manager instance's ``setProxy(_:)`` with the scroll view |
| 19 | +/// proxy from any scroll view reader in your view. |
17 | 20 | /// |
18 | | -/// To use it, inject an instance into a compatible scroll |
19 | | -/// view like `ScrollViewWithStickyHeader`, which will |
20 | | -/// register its internal proxy with the manager on appear. |
| 21 | +/// Once everything's done, you can use ``scroll(to:anchor:)`` |
| 22 | +/// to scroll to any defined targets within your scroll view. |
21 | 23 | /// |
22 | | -/// You can then call `scrollToHeader()` or |
23 | | -/// `scrollToContent()` from your view model or UI logic |
24 | | -/// to trigger animated scrolling actions. |
| 24 | +/// The ``ScrollViewWithStickyHeader`` has support for using |
| 25 | +/// this manager, but you can add it to any custom view. |
25 | 26 | /// |
26 | | -/// - Important: `ScrollManager` uses `ScrollViewReader` |
27 | | -/// under the hood, so the scrollable views must have |
28 | | -/// valid `.id(...)` values matching the internal targets. |
| 27 | +/// - Important: The manager uses a `ScrollViewReader` under |
| 28 | +/// the hood, so yoyr scroll view must apply valid `.id(...)` |
| 29 | +/// values to the header and content. |
29 | 30 | public class ScrollManager { |
30 | 31 |
|
31 | 32 | /// Creates a new scroll manager instance. |
32 | 33 | public init() { } |
33 | 34 |
|
34 | | - private var proxy: ScrollViewProxy? |
35 | | - |
36 | | - /// Scroll to the sticky header in the scroll view. |
37 | | - /// |
38 | | - /// - Parameter anchor: The anchor point to scroll to, |
39 | | - /// defaulting to `.top`. |
40 | | - public func scrollToHeader(anchor: UnitPoint = .top) { |
41 | | - withAnimation { |
42 | | - proxy?.scrollTo(ScrollTargets.header, anchor: anchor) |
43 | | - } |
44 | | - } |
45 | | - |
46 | | - /// Scroll to the main content in the scroll view. |
| 35 | + /// The currently configured scroll view proxy, if any. |
| 36 | + public private(set) var proxy: ScrollViewProxy? |
| 37 | + |
| 38 | + /// Scroll to any target within . |
47 | 39 | /// |
48 | | - /// - Parameter anchor: The anchor point to scroll to, |
49 | | - /// defaulting to `.top`. |
50 | | - public func scrollToContent(anchor: UnitPoint = .top) { |
| 40 | + /// - Parameters: |
| 41 | + /// - target: The target to scroll to. |
| 42 | + /// - anchor: The anchor point to scroll to, by default `.top`. |
| 43 | + public func scroll( |
| 44 | + to target: ScrollTarget, |
| 45 | + anchor: UnitPoint = .top |
| 46 | + ) { |
51 | 47 | withAnimation { |
52 | | - proxy?.scrollTo(ScrollTargets.content, anchor: anchor) |
| 48 | + proxy?.scrollTo(target, anchor: anchor) |
53 | 49 | } |
54 | 50 | } |
55 | 51 |
|
56 | 52 | /// Set the internal scroll proxy. |
57 | | - /// |
58 | | - /// This method is intended for internal use by views |
59 | | - /// like `ScrollViewWithStickyHeader`. |
60 | | - /// |
61 | | - /// - Parameter proxy: The `ScrollViewProxy` to store. |
62 | | - internal func setProxy(_ proxy: ScrollViewProxy) { |
| 53 | + public func setProxy(_ proxy: ScrollViewProxy) { |
63 | 54 | self.proxy = proxy |
64 | 55 | } |
65 | 56 |
|
66 | 57 | /// Internal scroll target identifiers. |
67 | | - enum ScrollTargets { |
68 | | - static let header = "scrollkit-target-header" |
69 | | - static let content = "scrollkit-target-content" |
| 58 | + public enum ScrollTarget: String { |
| 59 | + case header, content |
| 60 | + } |
| 61 | +} |
| 62 | + |
| 63 | +public extension View { |
| 64 | + |
| 65 | + /// Register the view as a scroll target. |
| 66 | + func scrollTarget( |
| 67 | + _ target: ScrollManager.ScrollTarget |
| 68 | + ) -> some View { |
| 69 | + self.id(target) |
70 | 70 | } |
71 | 71 | } |
0 commit comments