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()
}
}