Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI not refresh my custom UIView with UIViewRepresentable & ObservableObject

I have

  1. created a simple ARClass: ObservableObject with @Published var name: String
  2. created a custom ARView: UIView that receives ARClass as a parameter and draws a Text view displaying ARClass.name in the center
  3. created UIViewRepresentable

and then used in SwiftUI View by using ARClass(color: .red, name: "Test Text") as an environment object in SceneDelegate

class ARClass: ObservableObject {
    @Published var bg: UIColor
    @Published var name: String
    
    init(color: UIColor, name: String) {
        self.bg = color
        self.name = name
    }
}
struct ARViewX: UIViewRepresentable {
    var ar: ARClass
    var frame: CGRect
    
    func updateUIView(_ uiView: ARView, context: Context) {
        uiView.frame = frame
        uiView.ar = ar
        uiView.setNeedsDisplay()
    }
    func makeUIView(context: Context) -> ARView {
        ARView(ar: ar, frame: frame)
    }
}
struct ContentView: View {
    @EnvironmentObject var ar: ARClass
    var body: some View {
        GeometryReader { g in
            VStack {
                Spacer()
                ARViewX(ar: self.ar, frame: CGRect(origin: .zero, size: .init(width: g.size.width, height: g.size.height/3)))
                    .padding()
                Spacer()
                Text(self.ar.name)
                Divider()
                TextField("Name", text: self.$ar.name)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .padding()
                Spacer()
            }
        }
    }
}

As I edited the TextField, the whole body is refreshed except the UIViewRepresentable. It always resets the 'name' variable to its initial value.

like image 854
Anan R Avatar asked Sep 28 '19 02:09

Anan R


Video Answer


1 Answers

After hours finally I know that the updateUIView(_ uiView: ARView, context: Context) in UIViewRepresentable never got called so I modified the ARClass like this:

class ARClass: ObservableObject {
    @Published var bg: UIColor
    @Published var name: String {
        didSet {
            if let onNameChange = onNameChange {
                onNameChange()
            }
        }
    }
    var onNameChange: (()->Void)?
    init(color: UIColor, name: String) {
        self.bg = color
        self.name = name
    }
}

And the UIViewRepresentable like this:

func updateUIView(_ uiView: ARView, context: Context) {
    uiView.frame = frame
    ar.onNameChange = {
        uiView.setNeedsDisplay()
    }
}
like image 96
Anan R Avatar answered Oct 13 '22 21:10

Anan R