How to Trigger Updateuiview of a Uiviewrepresentable

How do I trigger updateUIView of a UIViewRepresentable?

You need to create UIKit view inside makeUIView and via Binding pass only dependent data. That binding change, when related state - source of truth - changed, calls updateUIView, where you should update your UIKit view.

Here is simplified demo sketch only, to show concept (might have typos):

struct SpritzUIViewRepresentable : UIViewRepresentable{
@Binding var currentWord: SpritzSwiftWord
@Binding var backgroundColor: UIColor

func makeUIView(context: Context) -> SpritzSwiftView {
// create and configure view here
return SpritzSwiftView(frame: CGRect.zero) // frame does not matter here
}

func updateUIView(_ uiView: SpritzSwiftView, context: Context) {
// update view properties here from bound external data
uiView.backgroundColor = backgroundColor
uiView.updateWord(currentWord)
}
}

and button now should just change model data

    VStack {
Text("SpritzTest")
.padding()
SpritzUIViewRepresentable(backgroundColor: $backgroundColor, SpritzViewManager:$ssManager, currentWord: $currentWord)
.padding()
Button(action:
{
ssManager = SpritzSwiftManager(withText: "Text try one two three", andWordPerMinute: 200)

self.backgroundColor = .clear
ssManager.startReading { (word, finished) in
if !finished {
self.currentWord = word
}
}
})
{
Text("Start")
}

assuming updated properties

@State private var currentWord = SpritzSwiftWord(word: "")
@State private var backgroundColor = UIColor.white // whatever you want

Update UIView (inside UIViewRepresentable) when @Binding value changes

You don't need to use onReceive and watch the publisher -- updateUIView will be called when selectedValue changes, so you can use it directly:

struct ContentView: View {
@State private var selected = "3"

var body: some View {
CustomPickerView(data: ["1","2","3"], selectedValue: $selected)
Button("Choose 1") {
selected = "1"
}
}
}

struct CustomPickerView: UIViewRepresentable {

let data: [String]
@Binding var selectedValue: String

//makeCoordinator()
func makeCoordinator() -> CustomPickerView.Coordinator {
return CustomPickerView.Coordinator(self)
}

//makeUIView(context:)
func makeUIView(context: UIViewRepresentableContext<CustomPickerView>) -> UIPickerView {
let picker = UIPickerView(frame: .zero)
// allows rows to be compressed in swiftUI
picker.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
picker.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
// allows rows to be expanded
picker.setContentHuggingPriority(.defaultLow, for: .horizontal)
picker.dataSource = context.coordinator
picker.delegate = context.coordinator

picker.selectRow(data.firstIndex(of: selectedValue) ?? 0, inComponent: 0, animated: false)
return picker
}

func updateUIView(_ view: UIPickerView, context: UIViewRepresentableContext<CustomPickerView>) {
view.selectRow(data.firstIndex(of: selectedValue) ?? 0, inComponent: 0, animated: true)
}

class Coordinator : NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
1
}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
parent.data.count
}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
{
parent.data[row]
}

var parent: CustomPickerView

init(_ input : CustomPickerView) {
self.parent = input
}
}
}

iOS 14 SwiftUI UIViewRepresentable updateUIView doesn't detect ObservedObject changing?

You need to make ˋCustomTextViewˋ to listen to changes of ˋTmpTextViewModelˋ.

Just declare ˋtmpTextVMˋ in ˋCustomTextViewˋ as ˋ@ObservedOjectˋ.

struct CustomTextView: UIViewRepresentable {
@ObservedObject var tmpTextVM: TmpTextViewModel
//....
}

Update Binding in UIViewRepresentable

Looks like SwiftUI is doing some cleverness under the hood that isn't immediately obvious to us. Because you don't actually use your binding in updateUIView, it's not actually getting called.

However, if you update your code to the following:

func updateUIView(_ uiView: UIViewType, context: Context) {
print("Hello \(status)")
}

then you'll see that it does, in fact, get called.

PS - you can use status.toggle() instead of status = !status

SwiftUI: updateUIView() function in UIViewRepresentable causes app to freeze and CPU to spike

In your updateUIView, you're setting a value to self.height, which is a Binding. My guess is that the @Binding is connected to a property (either another @Binding or a @State on your surrounding view). So, whenever you set a new value to that @Binding, that triggers a refresh of the parent view. That, in turn, ends up calling updateUIView again, and you get into an infinite loop.

How to solve it probably depends on your architecture needs for the program. If you can get away with not having the parent know the height, you can probably solve it just by having the view update its own height.

You could also try only setting self.height to a new value if it != the old one -- that would probably short circuit the loop. But, you might end up with other side effects.

UIView does not update after updateUIView is called on UIViewRepresentable

I built the code to my iPhone and it worked on there. I guess it was just broken on the simulator.

How to update UIViewRepresentable with ObservableObject

To make sure your ObservedObject does not get created multiple times (you only want one copy of it), you can put it outside your UIViewRepresentable:

import SwiftUI
import MapKit

struct ContentView: View {
@ObservedObject var dataSource = DataSource()

var body: some View {
MyView(locationCoordinates: dataSource.locationCoordinates, value: dataSource.value)
}
}
class DataSource: ObservableObject {
@Published var locationCoordinates = [CLLocationCoordinate2D]()
var value: Int = 0

init() {
Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { timer in
self.value += 1
self.locationCoordinates.append(CLLocationCoordinate2D(latitude: 52, longitude: 16+0.1*Double(self.value)))
}
}
}

struct MyView: UIViewRepresentable {
var locationCoordinates: [CLLocationCoordinate2D]
var value: Int

func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}

func updateUIView(_ view: MKMapView, context: Context) {
print("I am being called!")
let newestCoordinate = locationCoordinates.last ?? CLLocationCoordinate2D(latitude: 52, longitude: 16)
let annotation = MKPointAnnotation()
annotation.coordinate = newestCoordinate
annotation.title = "Test #\(value)"
view.addAnnotation(annotation)
}
}


Related Topics



Leave a reply



Submit