1

I have a @Published parameter in my viewmodel like here:

struct TestView: View {
    let viewModel: ViewModel

    var body: some View {
        Text("aaa")
            .overlay(Color.clear.modifier(GeometryGetter(rect: viewModel.rect)))
    }
}

extension TestView {
    class ViewModel {
        @Published var rect: CGRect = .zero
    }
}

I want to connect this parameter to an inner view, so I thought I'd use @Binding like here:

public struct GeometryGetter: ViewModifier {
    @Binding var rect: CGRect
    
    public init(rect: Binding<CGRect>) {
        self._rect = rect
    }
    
    public func body(content: Content) -> some View {
        return GeometryReader { proxy -> Color in
            DispatchQueue.main.async {
                self.rect = proxy.frame(in: .global)
            }
            return Color.clear
        }
    }
}

But I'm getting an error:

Cannot convert value of type 'Published<CGRect>.Publisher' to expected argument type 'Binding<CGRect>'

What is the correct way to implement it?

2 Answers 2

1

You need to make view model as ObservableObject and in view add corresponding wrapper, then it is possible to pass binding to published property, like

struct TestView: View {
    @ObservedObject var viewModel: ViewModel

    var body: some View {
        Text("aaa")
            .overlay(Color.clear.modifier(GeometryGetter(rect: $viewModel.rect)))
    }
}

extension TestView {
    class ViewModel: ObservableObject {
        @Published var rect: CGRect = .zero
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

I'm still getting: "Cannot convert value of type 'Published<CGRect>.Publisher' to expected argument type 'Binding<CGRect>'"
All built fine here, see carefully how did you integrate.
Got it to compile. I missed the position of the $. Instead of $viewModel.rect I did viewModel.$rect. But it's not working yet, because things are constantly updating the values. If I put a break point inside some calculated vars that are affected by those @Published values, it stops the project, although I didn't touch the screen. If I don't use those Publish in the view model but put it as a State in the view, it works fine.
0

We don't use View Model objects in SwiftUI for view data. The View struct should be our primary encapsulation mechanism for view data and sometimes we use custom structs when we have multiple related vars and methods that we want to test independently. The use of @State and @Binding gives us all the benefits of reference types like objects but we can use super fast value types with the benefits of value semantics - learning this concept is fundamental to understanding SwiftUI. Thus replacing your class with a struct, your code should look something along the lines of this (not sure about your modifier though):

struct TestViewConfig {
    var rect: CGRect = .zero
    var someOtherVar = false

    func someFuncYoudLikeToTest() {

    }

    mutating func someFuncThatMakesAChange() {
        rect = .zero
    }
}

struct TestView: View {
    @State var config = TestViewConfig() // everytime TestView is recreated, e.g. when init in a parent body, it is supplied the old version of config so the changes are not lost between updates.

    var body: some View {
        Text("aaa")
            .overlay(Color.clear.modifier(GeometryGetter(rect: $config.rect)))
    }
}


public struct GeometryGetter: ViewModifier {
    @Binding var rect: CGRect // gives us write access to the config stored in @State
    
    public func body(content: Content) -> some View {
        return GeometryReader { proxy -> Color in
            rect = proxy.frame(in: .global)
            return Color.clear
        }
    }
}

For more info on this custom struct pattern watch WWDC 2020 Data Essentials in SwiftUI at 4:06

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.