Как обновить UIViewRepresentable с помощью ObservableObject

Я пытаюсь изучить Combine с SwiftUI, и я изо всех сил пытаюсь обновить свое представление (из UIKit) с помощью ObservableObject (ранее BindableObject). Проблема в том, что, очевидно, метод updateUIView не сработает после того, как объект @Published отправит уведомление о том, что он был изменен.

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)
    }
}

Как привязать этот массив locationCoordinates к представлению таким образом, чтобы новая точка фактически добавлялась при каждом обновлении?


person Nat    schedule 13.08.2019    source источник
comment
вы нашли способ сделать это, не вытаскивая его?   -  person Marty    schedule 25.02.2020


Ответы (2)


Чтобы ваш ObservedObject не создавался несколько раз (вам нужна только одна его копия), вы можете поместить его за пределы вашего 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)
    }
}
person kontiki    schedule 13.08.2019
comment
Я бы предпочел внедрить модель с помощью другой вещи (интерактора), которая не наследуется от класса View. Есть ли другой способ наблюдать за ним, не создавая структуру, унаследованную от View? То, что вы сделали, в основном оборачивает его вокруг, и, хотя он работает, он не показывает, как исправить саму проблему UIViewRepresentable. Кроме того, я не думаю, что ваше решение исправляет создание ObservedObject несколько раз. UIViewRepresentable создается только один раз здесь и там, поэтому он не создается несколько раз. Если вы имеете в виду, что он будет воссоздан после воссоздания представления, то это также произойдет в обоих случаях. - person Nat; 16.08.2019
comment
Да - у меня была такая же проблема, единственное решение заключалось в том, чтобы держать мой источник данных в env - иначе он будет воссоздан, независимо от того, где я его поместил. - person DogCoffee; 27.11.2019