Home > Mobile >  How do I implement data binding in a View Controller in SwiftUI?
How do I implement data binding in a View Controller in SwiftUI?

Time:09-22

I have a UIKit ViewController that's nested inside a SwiftUI view using ViewControllerRepresentable. The SwiftUI view manages a bit of state (an Int, in this example) that I want to display in the UIKit view. When the user taps a button in the SwiftUI parent view, the state change should be reflected in the UIKit view. I've tried using the @Binding property wrapper to keep the two in sync, but clearly I'm missing something, as my view controller's initialiser throws a compile-time error.

I'm quite new to iOS development so perhaps I'm going in the complete wrong direction here. Any help would be greatly appreciated.

The code is as follows (simplified):

struct ContentView: View {
    @State private var currentNumber: Int

    init(currentNumber: Int) {
        self.currentNumber = currentNumber
    }

    var body: some View {
        FancyLabelViewControllerRepresentable(currentNumber: self.$currentNumber)
        Button("Increment") {
            self.currentNumber  = 1
        }
    }
}

struct FancyLabelViewControllerRepresentable: UIViewControllerRepresentable {
    typealias UIViewControllerType = FancyLabelViewController

    @Binding var currentNumber: Int

    init(currentNumber: Binding<Int>) {
        self._currentNumber = currentNumber
    }

    func makeUIViewController(context: Context) -> FancyLabelViewController {
        let fancyLabel = FancyLabelViewController(number: self.currentNumber)
        fancyLabel.currentNumberInLabel = self.currentNumber
        return fancyLabel
    }

    func updateUIViewController(_ uiViewController: FancyLabelViewController, context: Context) {
        uiViewController.currentNumberInLabel = self.currentNumber
    }

}

class FancyLabelViewController: UIViewController {
    var label = UILabel()
    @Binding var currentNumberInLabel: Int

    init(number: Int) {
        // Error: 'self' used in property access 'currentNumberInLabel' before 'super.init' call
        self.currentNumberInLabel = number
        // Error: Property 'self.currentNumberInLabel' not initialized at super.init call
        super.init(nibName: nil, bundle: nil)
    }

    required init(coder: NSCoder) {
        fatalError("Not implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        label.text = "\(currentNumberInLabel)"
        view = label
    }
}

CodePudding user response:

I think you don't need the

@Binding var currentNumberInLabel: Int

because the UIViewControllerRepresentable already takes care of updating the currentNumberInLabel value, but you also needs to update the

label.text = "\(currentNumberInLabel)"

So I did something like

class FancyLabelViewController: UIViewController {
    var label = UILabel()
    var currentNumberInLabel: Int

    init(number: Int) {
        self.currentNumberInLabel = number
        super.init(nibName: nil, bundle: nil)
    }

    required init(coder: NSCoder) {
        fatalError("Not implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        label.text = "\(currentNumberInLabel)"
        view = label
    }
  
    func updateLabel() {
      label.text = "\(currentNumberInLabel)"
    }
}

and call updateLabel from UIViewControllerRepresentable as

func updateUIViewController(_ uiViewController: FancyLabelViewController, context: Context) {
    uiViewController.currentNumberInLabel = self.currentNumber
    uiViewController.updateLabel()
}
  • Related