-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #818 from kiwicom/817-horizontalscroll-should-info…
…rm-about-scroll-change `HorizontalScroll` currently scrolled item change
- Loading branch information
Showing
3 changed files
with
139 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
Sources/Orbit/Support/Layout/HorizontalScrollPosition.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import SwiftUI | ||
|
||
@available(iOS 14, *) | ||
struct HorizontalScrollPositionModifier: ViewModifier { | ||
|
||
@Binding var position: AnyHashable? | ||
@State private var isModified = false | ||
|
||
func body(content: Content) -> some View { | ||
HorizontalScrollReader { proxy in | ||
content | ||
.onChange(of: position) { | ||
if isModified { | ||
isModified = false | ||
return | ||
} | ||
proxy.scrollTo($0) | ||
} | ||
.onPreferenceChange(HorizontalScrollScrolledItemIDKey.self) { | ||
isModified = true | ||
position = $0 | ||
} | ||
} | ||
} | ||
} | ||
|
||
public extension View { | ||
|
||
/// Associates a binding to be updated when an Orbit HorizontalScroll view within this view scrolls. | ||
/// | ||
/// Use this modifier along with the ``identifier(_:)`` and ``HorizontalScroll`` to know the identity of the view that is actively scrolled. As the scroll view scrolls, the binding will be updated with the identity of the leading-most / top-most view. | ||
/// | ||
/// You can also write to the binding to scroll to the view with the provided identity. | ||
@available(iOS 14, *) | ||
func horizontalScrollPosition<Value>(id: Binding<Value?>) -> some View where Value: Hashable { | ||
modifier( | ||
HorizontalScrollPositionModifier( | ||
position: .init( | ||
get: { id.wrappedValue as AnyHashable? }, | ||
set: { id.wrappedValue = $0 as? Value } | ||
) | ||
) | ||
) | ||
} | ||
} | ||
|
||
// MARK: - Previews | ||
@available(iOS 14, *) | ||
struct HorizontalScrollPositionModifierPreviews: PreviewProvider { | ||
|
||
static var previews: some View { | ||
PreviewWrapper { | ||
StateWrapper(Int?.none) { $id in | ||
VStack(alignment: .leading, spacing: .medium) { | ||
Heading("Snapping", style: .title3) | ||
|
||
HorizontalScroll(spacing: .medium, itemWidth: .ratio(0.95)) { | ||
ForEach(0..<5) { index in | ||
Tile("Tile \(index)", description: "Tap to scroll to previous") { | ||
id = index - 1 | ||
} | ||
.identifier(index) | ||
} | ||
} | ||
.horizontalScrollPosition(id: $id) | ||
|
||
Heading("Non snapping", style: .title3) | ||
|
||
HorizontalScroll(isSnapping: false, spacing: .medium, itemWidth: .ratio(0.95)) { | ||
ForEach(0..<5) { index in | ||
Tile("Tile \(index)", description: "Tap to scroll to previous") { | ||
id = index - 1 | ||
} | ||
.identifier(index) | ||
} | ||
} | ||
.horizontalScrollPosition(id: $id) | ||
|
||
Text("Scrolled item index: \(id ?? -1)") | ||
|
||
Heading("Scroll to:", style: .title3) | ||
|
||
HStack { | ||
ForEach(0..<5) { index in | ||
Button("\(index)") { | ||
id = index | ||
} | ||
} | ||
} | ||
} | ||
.screenLayout() | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters