Created
December 21, 2024 11:10
-
-
Save dkasaj/77b7c68e0b5d01d814382c03a824a6d1 to your computer and use it in GitHub Desktop.
SwiftUI ScrollView Centered Items with ScrollTargetBehavior
This file contains hidden or 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
import SwiftUI | |
struct ContentView: View { | |
private let colors: [Color] = [.red, .orange, .yellow, .green, .blue, .indigo, .purple] | |
private let itemDimension = UIScreen.main.bounds.width * 0.7 | |
private let itemSpacing = CGFloat(16) | |
private let scrollViewHorizontalInset = CGFloat(16) | |
var body: some View { | |
ScrollView(.horizontal, showsIndicators: false) { | |
LazyHStack(spacing: itemSpacing) { | |
ForEach(colors, id: \.self) { color in | |
VStack { | |
Rectangle() | |
.foregroundStyle(color) | |
.frame(width: itemDimension, height: itemDimension * 2/3) | |
Text(color.description.capitalized) | |
} | |
} | |
} | |
.padding(.horizontal, scrollViewHorizontalInset) | |
} | |
.scrollTargetBehavior(CenteredBehavior(itemWidth: itemDimension, | |
itemSpacing: itemSpacing, | |
scrollViewHorizontalInset: scrollViewHorizontalInset)) | |
} | |
} | |
#Preview { | |
ContentView() | |
} | |
struct CenteredBehavior: ScrollTargetBehavior { | |
private let itemWidth: CGFloat | |
private let itemSpacing: CGFloat | |
private let scrollViewHorizontalInset: CGFloat | |
private let widthOfItemWithSpacing: CGFloat | |
init(itemWidth: CGFloat, itemSpacing: CGFloat, scrollViewHorizontalInset: CGFloat) { | |
self.itemWidth = itemWidth | |
self.itemSpacing = itemSpacing | |
self.scrollViewHorizontalInset = scrollViewHorizontalInset | |
widthOfItemWithSpacing = itemWidth + itemSpacing | |
} | |
func updateTarget(_ target: inout ScrollTarget, context: TargetContext) { | |
let itemIndex = round((target.rect.origin.x - scrollViewHorizontalInset) / widthOfItemWithSpacing) | |
let itemMinX = itemIndex * widthOfItemWithSpacing | |
let amountToShiftLeft = (context.containerSize.width - itemWidth) / 2 | |
let newMinX = scrollViewHorizontalInset + itemMinX - amountToShiftLeft | |
target.rect.origin.x = newMinX | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment