Как вызвать метод в представлении в SwiftUI - PullRequest
0 голосов
/ 22 февраля 2020

Просто приступаю к работе с SwiftUI.

У меня есть GoogleMapsView в ContentView, использующий CLLocationManager I, фиксирующий события в классе AppDelegate или SceneDelegate с помощью расширения их с помощью CLLocationManagerDelegate.

Как я могу вызвать метод в GoogleMapsView из AppDelegate или SceneDelegate?

В этом случае я хочу вызвать .animate метод, когда событие изменения местоположения отправляется в экземпляр AppDelegate через CLLocationManagerDelegate, но вопрос действительно более общий c.

Ответы [ 2 ]

1 голос
/ 29 февраля 2020

Я сделал и реализацию CLLocationManager и MKMapView, и она почти такая же, как карты, надеюсь, это поможет вам:

Краткий ответ : объявив @Binding var foo: Any, вы сможете вносить изменения в GoogleMapView каждый раз, когда меняется foo, в этом случае foo - ваше местоположение, поэтому вы можете вызывать animate при каждом обновлении foo.

Длинный ответ :

Сначала я создал Mapview, который соответствует протоколу UIViewRepresentable, как и вы, но добавив переменную @Binding, это мой " триггер ".

MapView :

struct MapView: UIViewRepresentable {
    @Binding var location: CLLocation // Create a @Binding variable that keeps the location where I want to place the view, every time it changes updateUIView will be called
    private let zoomMeters = 400

    func makeUIView(context: UIViewRepresentableContext<MapView>) -> MKMapView {
        let mapView = MKMapView(frame: UIScreen.main.bounds)
        return mapView
    }

    func updateUIView(_ mapView: MKMapView, context: Context) {
        //When location changes, updateUIView is called, so here I move the map:
        let region = MKCoordinateRegion(center: location.coordinate,
                                        latitudinalMeters: CLLocationDistance(exactly: zoomMeters)!,
                                        longitudinalMeters: CLLocationDistance(exactly: zoomMeters)!)
        mapView.setRegion(mapView.regionThatFits(region), animated: true)
    }
}

Затем я поместил свой MapView в мой ContentView, передав аргумент местоположения, который я объясню следующим образом:

ContentView :

struct ContentView: View {

    @ObservedObject var viewModel: ContentViewModel

    var body: some View {
        VStack {
            MapView(location: self.$viewModel.location)
        }
    }
}

В моей ViewModel я обрабатываю изменения местоположения с помощью делегата, вот код с более подробной информацией в комментариях:

class ContentViewModel: ObservableObject {
    //location is a Published value, so the view is updated every time location changes
    @Published var location: CLLocation = CLLocation.init()

    //LocationWorker will take care of CLLocationManager...
    let locationWorker: LocationWorker = LocationWorker()

    init() {
        locationWorker.delegate = self
    }

}

extension ContentViewModel: LocationWorkerDelegate {
    func locationChanged(lastLocation: CLLocation?) {
        //Location changed, I change the value of self.location, it is a @Published value so it will refresh the @Binding variable inside MapView and call MapView.updateUIView
        self.location = CLLocation.init(latitude: lastLocation!.coordinate.latitude, longitude: lastLocation!.coordinate.latitude)
    }
}

И, наконец, LocationWorker, который заботится о CLLocationManager ():

class LocationWorker: NSObject, ObservableObject  {

    private let locationManager = CLLocationManager()
    var delegate: LocationWorkerDelegate?

    let objectWillChange = PassthroughSubject<Void, Never>()

    @Published var locationStatus: CLAuthorizationStatus? {
        willSet {
            objectWillChange.send()
        }
    }

    @Published var lastLocation: CLLocation? {
        willSet {
            objectWillChange.send()
        }
    }

    override init() {
        super.init()
        self.locationManager.delegate = self
        //...
    }
}

protocol LocationWorkerDelegate {
    func locationChanged(lastLocation: CLLocation?)
}

extension LocationWorker: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        self.lastLocation = location
        //When location changes: I use my delegate ->
        if delegate != nil {
            delegate!.locationChanged(lastLocation: lastLocation)
        }
    }
}
0 голосов
/ 28 февраля 2020

Вместо того, чтобы вызывать метод View напрямую извне, вы должны немного пересмотреть свою логику c и просто изменить где-то state где-нибудь и позволить View обновить себя. Взгляните на этот алгоритм:


Класси c (и худший) способ:

  1. Местоположение изменено
  2. Метод делегата, вызываемый в приложении делегат (лучше рефакторинг в другое место)
  3. Делегат приложения вызывает метод непосредственно на view (Вы должны передать ссылку на этот view вплоть до делегата приложения)

Хотя вышеприведенный алгоритм - то, что вы ищете изначально, это не лучший способ, и я его вообще не рекомендую! Но это будет работать ??‍♂️


Способ SwiftUI:

  1. Местоположение изменено
  2. Метод делегата, вызванный в ответственном объекте (возможно, экземпляр менеджера местоположений синглтона ??‍♂️)
  3. Менеджер местоположений обновляет где-то State. (может быть переменная ObservedObject внутри себя или EnvironmentObject или et c.)
  4. Все представления, которые подписались на изменения этого свойства, будут уведомлять об изменениях
  5. Все уведомленные представления обновятся сами.

Вот как это должно быть сделано. Но есть не только один способ реализовать это, и вы должны учитывать ваши предпочтения, чтобы выбрать лучшее для вас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...