I am trying to wrap UIScrollView in SwiftUI for some additional functionality. I want to access contentOffset property of UIScrollView and assign it to some property (Published or Binding) in SwiftUI, but SwiftUI ( my ContentView) is not detecting any property changes. Can you help me get it right ?
This is my UIScrollView
import SwiftUI
import UIKit
struct MyScrollView<Content: View>: UIViewRepresentable {
@ObservedObject var viewModel: ViewModel
@ViewBuilder var content: () -> Content
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UIScrollViewDelegate {
var parent: MyScrollView
init(_ parent: MyScrollView) {
self.parent = parent
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
parent.viewModel.scrollviewContentOffset = scrollView.contentOffset.y
}
}
func makeUIView(context: Context) -> UIScrollView {
let scrollView = UIScrollView()
scrollView.delegate = context.coordinator
scrollView.isScrollEnabled = true
let child = UIHostingController(rootView: content())
scrollView.addSubview(child.view)
let newSize = child.view.sizeThatFits(CGSize(width: UIScreen.screenWidth, height: UIScreen.screenHeight))
child.view.frame = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
scrollView.contentSize = newSize
return scrollView
}
func updateUIView(_ uiView: UIScrollView, context: Context) {
//
}
typealias UIViewType = UIScrollView
}
and this is my ContentView
import SwiftUI
class ViewModel: ObservableObject {
@Published var scrollviewContentOffset = CGFloat.zero
}
struct ContentView: View {
@StateObject private var viewModel = ViewModel()
var body: some View {
MyScrollView(viewModel: viewModel) {
ForEach(0..<100) { i in
Text("\(i)")
.offset(y: viewModel.scrollviewContentOffset / CGFloat(i 1)
}
}
}
}
CodePudding user response:
You're only capturing the initial content when creating your MyScrollView, and not updating it on view model change.
You need to set updated content inside updateUIView. For example you can store hostingController inside Coordinator:
struct MyScrollView<Content: View>: UIViewRepresentable {
@StateObject var viewModel: ViewModel
@ViewBuilder var content: () -> Content
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UIScrollViewDelegate {
let parent: MyScrollView
var hostingController: UIHostingController<Content>!
init(_ parent: MyScrollView) {
self.parent = parent
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
parent.viewModel.scrollviewContentOffset = scrollView.contentOffset.y
}
}
func makeUIView(context: Context) -> UIScrollView {
let scrollView = UIScrollView()
scrollView.delegate = context.coordinator
scrollView.isScrollEnabled = true
let child = UIHostingController(rootView: content())
context.coordinator.hostingController = child
scrollView.addSubview(child.view)
let newSize = child.view.sizeThatFits(CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
child.view.frame = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
scrollView.contentSize = newSize
return scrollView
}
func updateUIView(_ uiView: UIScrollView, context: Context) {
context.coordinator.hostingController.rootView = content()
}
}
