Hello Swift Community,
I recently wrote an article on Medium about scrolling to a specific content offset in SwiftUI. Since SwiftUI (below iOS 17) doesn’t have a built-in way to programmatically scroll to a specific point (like UIScrollView in UIKit), I created a custom solution using a `ViewModifier'.
You can find the full article here: How to Scroll to a Specific Content Offset in SwiftUI.
Implementation details:
fileprivate struct ScrollContentOffsetModifier: ViewModifier {
@Binding var contentOffset: CGPoint
func body(content: Content) -> some View {
content
.background {
InternalScrollViewHelper(contentOffset: $contentOffset)
}
}
}
extension View {
func scrollToOffset(contentOffset: Binding<CGPoint>) -> some View {
return modifier(ScrollContentOffsetModifier(contentOffset: contentOffset))
}
}
fileprivate struct InternalScrollViewHelper: UIViewRepresentable {
@Binding var contentOffset: CGPoint
@State private var scrollView: UIScrollView?
func makeUIView(context: Context) -> some UIView {
let view = ScrollViewIdentifier()
view.scrollViewCompletion = { scrollView in
self.scrollView = scrollView
}
return view
}
func updateUIView(_ uiView: UIViewType, context: Context) {
scrollView?.setContentOffset(contentOffset, animated: true)
}
}
Identifying the ScrollView
fileprivate final class ScrollViewIdentifier: UIView {
var scrollViewCompletion: ((UIScrollView) -> Void)?
override init(frame: CGRect) {
super.init(frame: frame)
}
override func didMoveToWindow() {
guard let scrollView = superview?.superview?.superview as? UIScrollView else {
return
}
self.scrollViewCompletion?(scrollView)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
}
Usage
ScrollView {
LazyVStack {
ForEach(0..<40, id: \.self) { val in
Text("Title \(String(val))")
.padding()
}
}
.scrollToOffset(contentOffset: $contentOffset)
}
Discussion Points:
- Have you faced similar challenges when working with
ScrollViewin SwiftUI? If so, how did you solve it? - Do you think Apple should provide a built-in solution for this in future SwiftUI updates?
- What are some improvements or optimizations you would suggest for my implementation?
I am looking forward to your feedback and insights!