4.8 C
New York
Saturday, April 12, 2025

ios – SwiftUI wrapping UIScrollView in UIViewControllerRepresentable – Easy methods to appropriately inset scrollbars?


The SwiftUI ScrollView lacks some options I would like and so I created a customized MyScrollView primarily based on UIScrollView wrapped inside a UIViewControllerRepresentable. Whereas this works largely tremendous, the scrollbars are usually not positioned appropriately when .ignoreSaveArea() is ready on the MyScrollView. Is there one thing mistaken with my code or is that this some bug in SwiftUI? How can this be solved?

enter image description here

var physique: some View {
    VStack {
        Textual content("Some Header Content material")
        
        MyScrollView {
            VStack {
                ForEach(0..<100, id: .self) { index in
                    Textual content("Line (index)")
                        .body(maxWidth: .infinity)
                        .background(.yellow)
                }
            }
        }
        .ignoresSafeArea()
    }
}

On this setup, the scrollbar doesn’t finish on the high of the scrollview however a couple of pixels beneath. This isn’t the case when both Textual content("Some Header Content material") or .ignoresSafeArea() is eliminated. Nonetheless, I would like the header and the protected space ought to be ignored…

Is there one thing mistaken with how the UIScrollView is used / positioned in my MyScrollView? Is the overall implementation of MyScrollView appropriate, or am I doing one thing mistaken?

struct MyScrollView: UIViewControllerRepresentable {
    let content material: Content material
    
    init(@ViewBuilder content material: () -> Content material) {
        self.content material = content material()
    }
    
    func makeUIViewController(context: Context) -> UIViewController {
        let scrollViewVC = UIViewController()
        scrollViewVC.view.backgroundColor = .clear
        
        let scrollView = UIScrollView()
        scrollView.backgroundColor = .clear
        
        let contentVC = UIHostingController(rootView: self.content material)
        contentVC.view.backgroundColor = .clear
        
        context.coordinator.contentVC = contentVC
        context.coordinator.scrollView = scrollView
        
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollViewVC.view.addSubview(scrollView)
        
        NSLayoutConstraint.activate([
            scrollView.topAnchor.constraint(equalTo: scrollViewVC.view.topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: scrollViewVC.view.bottomAnchor),
            scrollView.leadingAnchor.constraint(equalTo: scrollViewVC.view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: scrollViewVC.view.trailingAnchor)
        ])
        
        
        contentVC.willMove(toParent: scrollViewVC)
        scrollViewVC.addChild(contentVC)
        
        contentVC.view.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(contentVC.view)
        NSLayoutConstraint.activate([
            contentVC.view.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
            contentVC.view.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
            contentVC.view.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
            contentVC.view.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
            contentVC.view.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor)
        ])
        
        contentVC.didMove(toParent: scrollViewVC)
        
        return scrollViewVC
    }
    
    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        context.coordinator.contentVC?.rootView = content material
    }
    
    
    func makeCoordinator() -> Coordinator {
        return Coordinator()
    }
    
    class Coordinator {
        var contentVC: UIHostingController?
        var scrollView: UIScrollView?
        
        init() {
            //...
        }
    }
}

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles