Scrolling to a Specific Content Offset in SwiftUI

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:

  1. Have you faced similar challenges when working with ScrollView in SwiftUI? If so, how did you solve it?
  2. Do you think Apple should provide a built-in solution for this in future SwiftUI updates?
  3. What are some improvements or optimizations you would suggest for my implementation?

I am looking forward to your feedback and insights!

Hi Swift Community,

I recently wrote an article on Medium about implementing a collapsible header in SwiftUI. The article demonstrates how to collapse and expand a header view based on the scroll position using a custom CollapsableHeader component. You can check out the full article here: Implementing a Collapsible Header in SwiftUI.

Key Highlights from the Article:

  • The CollapsableHeader component allows you to pass a header view and ScrollView and automatically manages the expand/collapse behavior.
  • The enum HeaderState (expanded, collapsed) tracks the header state.
  • The header's height adjusts dynamically based on the ScrollView's content offset, creating a seamless collapsing effect.

Discussion Points:

  1. How do you handle collapsible headers in your SwiftUI projects? Are there alternative approaches that work well for you?
  2. Have you found any limitations or performance concerns with the approach I used? If so, how would you improve it?
  3. Do you think SwiftUI could benefit from more built-in features for handling collapsible elements and scrolling interactions?

I’d love to hear your feedback on this implementation and your experiences with similar features in SwiftUI.