Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update UIViewRepresentable with ObservableObject

I'm trying to learn Combine with SwiftUI and I'm struggling how to update my view (from UIKit) with ObservableObject (previously BindableObject). The issue is that, obviously, method updateUIView will not fire once the @Published object sends the notification it was changed.

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 {
    @ObservedObject var dataSource = DataSource()

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

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

How to bind that locationCoordinates array to the view in such a way, that a new point is in fact added each time it refreshes?

like image 271
Nat Avatar asked Aug 13 '19 12:08

Nat


2 Answers

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)
    }
}
like image 108
kontiki Avatar answered Nov 19 '22 04:11

kontiki


this solution worked for me but with EnvironmentObject https://gist.github.com/svanimpe/152e6539cd371a9ae0cfee42b374d7c4

like image 1
Krzysztof Dziuba Avatar answered Nov 19 '22 04:11

Krzysztof Dziuba