I am trying to make a simple SWiftUI ScrollView where I can set and get the value of the ScrollView bounds offset via a binding. I have the following which compiles and works fine as a ScrollView but I am unable to actually set and get the offset and propagate it back to the ContentView where the ScrollView is hosted.
I have the following:
struct MyScrollView<Content>: NSViewRepresentable where Content: View {
private var content: Content
let offset: Binding<CGFloat>
init(offset: Binding<CGFloat>, @ViewBuilder content: () -> Content) {
self.content = content()
self.offset = offset
}
func makeNSView(context: NSViewRepresentableContext<MyScrollView>) ->TheScrollView {
let view = TheScrollView(offset: offset)
view.hasVerticalScroller = true
view.hasHorizontalScroller = true
let document = NSHostingView(rootView: content)
document.translatesAutoresizingMaskIntoConstraints = false
view.documentView = document
return view
}
func updateNSView(_ view: TheScrollView, context: NSViewRepresentableContext<MyScrollView>) {
}
}
class TheScrollView: NSScrollView, ObservableObject{
private var subscriptions: Set<AnyCancellable> = []
var offset: Binding<CGFloat>
init(offset: Binding<CGFloat>){
self.offset = offset
super.init(frame: .zero)
NotificationCenter.default
.publisher(for: NSScrollView.boundsDidChangeNotification, object: self.contentView.documentView)
.sink() { _ in
let view = self.contentView
print(view.bounds.origin.y) // <- I do get this
self.offset.wrappedValue = view.bounds.origin.y // This does nothing
}
.store(in: &subscriptions)
}
required init?(coder: NSCoder){
fatalError("init(coder:) has not been implemented")
}
}
MyScrollView is hosted in a contentView like this:
import SwiftUI
import Combine
struct ContentView: View{
@State var offset: CGFloat = 10.0{
didSet{
print("Offset \(offset)")
}
}
var body: some View{
MyScrollView(offset: $offset){
ZStack{
Rectangle().foregroundColor(.clear).frame(width: 1200, height: 1000)
Rectangle().foregroundColor(.blue).frame(width: 100, height: 100)
}
}
}
}
As you can see the offset value is passed from the @State var into MyScollView and then into TheScrollView, which is a subclass of NSScrollView. From there I have a simple notification to get the bounds change and set the binding. However setting the binding does nothing to the actual value in the binding and it definitely doesn't propagate back to the ContentView. Also, the address of offset changes up the hierarchy so it looks like I am passing a Binding to a Binding into TheScrollView rather than the original binding, but I cant seem to fix it.
Can anyone see what I am doing wrong?