Skip to content

Commit 795490c

Browse files
committed
Adjust scroll manager
1 parent 93c96b0 commit 795490c

File tree

3 files changed

+50
-58
lines changed

3 files changed

+50
-58
lines changed

Demo/Demo/DemoScreen.swift

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,23 +52,15 @@ struct DemoScreen<HeaderView: View>: View {
5252
.previewHeaderContent()
5353
.opacity(1 - visibleHeaderRatio)
5454
}
55-
ToolbarItem(placement: .topBarTrailing) {
56-
Button {
57-
scrollManager.scrollToContent()
58-
} label: {
59-
Label("Scroll to content", systemImage: "hand.point.down")
60-
.labelStyle(.iconOnly)
61-
}
62-
.buttonStyle(.plain)
63-
}
64-
ToolbarItem(placement: .topBarTrailing) {
65-
Button {
66-
scrollManager.scrollToHeader()
67-
} label: {
68-
Label("Scroll to header", systemImage: "hand.point.up")
69-
.labelStyle(.iconOnly)
55+
ToolbarItemGroup(placement: .topBarTrailing) {
56+
Menu("Scroll to...") {
57+
Button("Header") {
58+
scrollManager.scroll(to: .header)
59+
}
60+
Button("Content") {
61+
scrollManager.scroll(to: .content)
62+
}
7063
}
71-
.buttonStyle(.plain)
7264
}
7365
}
7466
.toolbarBackground(.hidden)

Sources/ScrollKit/Helpers/ScrollManager.swift

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,64 +8,64 @@
88

99
import SwiftUI
1010

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.
1313
///
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.
1720
///
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.
2123
///
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.
2526
///
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.
2930
public class ScrollManager {
3031

3132
/// Creates a new scroll manager instance.
3233
public init() { }
3334

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 .
4739
///
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+
) {
5147
withAnimation {
52-
proxy?.scrollTo(ScrollTargets.content, anchor: anchor)
48+
proxy?.scrollTo(target, anchor: anchor)
5349
}
5450
}
5551

5652
/// 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) {
6354
self.proxy = proxy
6455
}
6556

6657
/// 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)
7070
}
7171
}

Sources/ScrollKit/ScrollViewWithStickyHeader.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,10 @@ private extension ScrollViewWithStickyHeader {
161161
VStack(spacing: 0) {
162162
scrollHeader
163163
.opacity(0)
164-
.id(ScrollManager.ScrollTargets.header)
164+
.scrollTarget(.header)
165165
content()
166166
.frame(maxHeight: .infinity)
167-
.id(ScrollManager.ScrollTargets.content)
167+
.scrollTarget(.content)
168168
}
169169
}
170170
.onAppear {

0 commit comments

Comments
 (0)