Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding optional selectedIndex binding to allow for programmatically switching views #15

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ For more examples, see `SnapToScrollDemo/ContentView.swift`.

## Configuration

`HStackSnap` comes with two customizable properties:
`HStackSnap` comes with some customizable properties:

- `alignment`: The way you'd like your elements to be arranged.
- `leading(CGFloat)`: Aligns your child views to the leading edge of `HStackSnap`. This configuration supports elements of various sizes, so long as they don't take up all available horizontal space (which would extend beyond the screen). Use the value to set the size of the left offset.
- `center(CGFloat)`: Automatically aligns your child view to the center of the screen, using the offset value you've provided. This is accomplished with inside of the `.snapAlignmentHelper` which sets the frame width based on the available space. Note that setting your own width elsewhere may produce unexpected layouts.
- `coordinateSpace`: Option to set custom name for the coordinate space, in the case you're using multiple `HStackSnap`s of various sizes. If you use this, set the same value in `.snapAlignmentHelper`.
- `selectedIndex`: (Optional) A @State variable binding e.g. $selectedIndex, which can be used programatically slide between Views.

`.snapAlignmentHelper` comes with two options as well:

Expand Down
5 changes: 5 additions & 0 deletions Sources/HStackSnap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ public struct HStackSnap<Content: View>: View {

// MARK: Lifecycle

@Binding var selectedIndex: Int?

public init(
alignment: SnapAlignment,
selectedIndex: Binding<Int>? = nil,
spacing: CGFloat? = nil,
coordinateSpace: String = "SnapToScroll",
@ViewBuilder content: @escaping () -> Content,
eventHandler: SnapToScrollEventHandler? = .none) {

self.content = content
self._selectedIndex = selectedIndex != nil ? Binding<Int?>(selectedIndex!) : .constant(nil)
self.alignment = alignment
self.leadingOffset = alignment.scrollOffset
self.spacing = spacing
Expand All @@ -34,6 +38,7 @@ public struct HStackSnap<Content: View>: View {

HStackSnapCore(
leadingOffset: leadingOffset,
selectedIndex: $selectedIndex,
spacing: spacing,
coordinateSpace: coordinateSpace,
content: content,
Expand Down
12 changes: 12 additions & 0 deletions Sources/Views/HStackSnapCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ public typealias SnapToScrollEventHandler = ((SnapToScrollEvent) -> Void)
public struct HStackSnapCore<Content: View>: View {
// MARK: Lifecycle

@Binding var selectedIndex: Int?

public init(
leadingOffset: CGFloat,
selectedIndex: Binding<Int?>? = nil,
spacing: CGFloat? = nil,
coordinateSpace: String = "SnapToScroll",
@ViewBuilder content: @escaping () -> Content,
eventHandler: SnapToScrollEventHandler? = .none) {

self.content = content
self._selectedIndex = selectedIndex ?? .constant(nil)
self.targetOffset = leadingOffset
self.spacing = spacing
self.scrollOffset = leadingOffset
Expand Down Expand Up @@ -90,6 +94,14 @@ public struct HStackSnapCore<Content: View>: View {
eventHandler?(.didLayout(layoutInfo: itemScrollPositions))
}
})
.onChange(of: selectedIndex) { newValue in
// Update state
withAnimation(.easeOut(duration: 0.2)) {
scrollOffset = snapLocations[selectedIndex] ?? 0
}
prevScrollOffset = scrollOffset
eventHandler?(.didLayout(layoutInfo: itemScrollPositions))
}
.contentShape(Rectangle())
.gesture(snapDrag)
}
Expand Down