skip to Main Content

I’m making a class that returns its editor view.
But got an error

Cannot convert value of type ‘Published.Publisher’ to expected
argument type ‘Binding’

This is simple test code for Playgrounds.

import SwiftUI
import PlaygroundSupport


struct EditorView: View {
    @Binding var text: String

    var body: some View {
        HStack {
            Text("Name:")
            TextField("Jerry", text: $text)
        }
    }
}

class MyModel: ObservableObject {
    @Published var name: String = "Tom"

    func editorView() -> some View {
        EditorView(text: $name) // [ERROR] Cannot convert value of type 'Published.Publisher' to expected argument type 'Binding<String>'
    }
}

struct ContentView: View {
    @StateObject var model: MyModel = .init()

    var body: some View {
        model.editorView()
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
            .previewLayout(.fixed(width: 375, height: 400))
    }
}

let viewController = UIHostingController(rootView: ContentView())
let nav = UINavigationController(rootViewController: viewController)
PlaygroundPage.current.liveView = nav

What is wrong?


2022.03.23 Added

I tested to make a view without binding, it works. I think that the class can create a view with its property. But can’t pass the @Binding.

import SwiftUI
import PlaygroundSupport


struct EditorView: View {
    @Binding var text: String

    var body: some View {
        HStack {
            Text("Name:")
            TextField("Jerry", text: $text)
        }
    }
}

// Just display the name parameter
struct DispView: View {
    let name: String
    var body: some View {
        Text(name)
    }
}

class MyModel: ObservableObject {
    @Published var name: String = "Tom"

    @ViewBuilder
    func editorView() -> some View {
//      EditorView(text: $name) // Error at this line.
        DispView(name: name) // Pass the property.
    }
}

struct ContentView: View {
    @StateObject var model: MyModel = .init()

    var body: some View {
        model.editorView()
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
            .previewLayout(.fixed(width: 375, height: 400))
    }
}

let viewController = UIHostingController(rootView: ContentView())
let nav = UINavigationController(rootViewController: viewController)
PlaygroundPage.current.liveView = nav

2

Answers


  1. Binding is provided by StateObject wrapper (on ObservableObject), and a view should be created in body (ie. in view hierarchy), so it should look like

    struct ContentView: View {
        @StateObject var model: MyModel = .init()
    
        var body: some View {
            EditorView(text: model.$name)     // << here !!
        }
    }
    

    Note: alternate would be to make EditorView dependent to MyModel directly and then inside such view you could create a wrapper which would provide needed binding.

    Login or Signup to reply.
  2. You create EditorView in wrong place, put it in your ContentView instead of your ViewModel. ViewModel is for holding data (or model) instead of View. I believe SwiftUI is not designed for carrying view along.

    struct ContentView: View {
        @StateObject var model: MyModel = .init()
    
        var body: some View {
            editorView
        }
    }
    
    extension ContentView {
        @ViewBuilder
        var editorView: some View {
            EditorView(text: $model.name)
        }
    }
    

    If you still want to put it in your ViewModel, this should be the working code. You can create new Binding as parameter for your view initializer.

    @ViewBuilder
    func editorView() -> some View {
        EditorView(text: 
            Binding { name } set: { newName in self.name = newName }
        )
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search