У меня есть этот код Swift ниже, который я использовал с раскадровками, и теперь хочу повторно использовать с SwiftUI. Он использует GeoFire для запроса местоположений в Firestore в пределах определенного радиуса radiusInM в соответствии с документацией Firebase.
Я хочу опубликовать «местоположения» массива в представлении, которое должно отображать их на карте, но в то время как массив заполняется местоположениями (с «self.locations.append (location)»), это не приводит к тому, что это наблюдается Вид.
Что является причиной этого? Есть ли лучший способ сделать это?
import Foundation
import CoreLocation
import FirebaseFirestore
import GeoFire
struct Location: Identifiable {
let id = UUID()
let name: String
let coordinate: CLLocationCoordinate2D
}
class LocationsViewModel: NSObject, ObservableObject {
@Published var locations: [Location] = []
// location
var lat: Double = 0.0
var lng: Double = 0.0
// firestore
var db: Firestore!
// Location records from Firestore
var matchingDocs = [QueryDocumentSnapshot]()
func getLocationsOnButton() {
getLocations(completion: {
print("*** finished all queries and getting locations ***")
for i in self.matchingDocs {
self.lat = i["lat"] as! Double
self.lng = i["lng"] as! Double
let location = Location(name: "Test", coordinate: CLLocationCoordinate2D(latitude: self.lat, longitude: self.lng))
self.locations.append(location)
}
})
}
func getLocations(completion: @escaping () -> Void) {
db = Firestore.firestore()
let center = CLLocationCoordinate2D(latitude: lat, longitude: lng)
let radiusInM: Double = 1000
var queriesFinished = 0
matchingDocs.removeAll()
// Each item in 'bounds' represents a startAt/endAt pair. We have to issue
// a separate query for each pair. There can be up to 9 pairs of bounds
// depending on overlap, but in most cases there are 4.
let queryBounds = GFUtils.queryBounds(forLocation: center,
withRadius: radiusInM)
let queries = queryBounds.map { bound -> Query in
return db.collection("places")
.order(by: "geohash")
.start(at: [bound.startValue])
.end(at: [bound.endValue])
}
let numOfQueries = queries.count
print("There are", numOfQueries, "queries")
// Collect all the query results together into a single list
func getDocumentsCompletion(snapshot: QuerySnapshot?, error: Error?) -> () {
guard let documents = snapshot?.documents else {
print("Unable to fetch snapshot data. \(String(describing: error))")
return
}
for document in documents {
let lat = document.data()["lat"] as? Double ?? 0
let lng = document.data()["lng"] as? Double ?? 0
let coordinates = CLLocation(latitude: lat, longitude: lng)
let centerPoint = CLLocation(latitude: center.latitude, longitude: center.longitude)
// We have to filter out a few false positives due to GeoHash accuracy, but most will match
let distance = GFUtils.distance(from: centerPoint, to: coordinates)
if distance <= radiusInM {
print("appending ", document.documentID)
if !matchingDocs.contains(document) {
matchingDocs.append(document)
}
}
}
queriesFinished += 1
print("Finished query # ", queriesFinished)
allDone()
}
// After all callbacks have executed, matchingDocs contains the result.
for query in queries {
print("Processing query")
query.getDocuments(completion:getDocumentsCompletion)
}
func allDone() {
if queriesFinished == numOfQueries {
print("Completed all queries")
completion()
}
}
}
}
Это вид
struct MapView: View {
@StateObject private var viewModel = MapViewModel()
@StateObject private var locationsViewModel = LocationsViewModel()
var body: some View {
Map(coordinateRegion: $viewModel.region, showsUserLocation: true, annotationItems: locationsViewModel.locations) { location in
MapMarker(coordinate: location.coordinate)
}
.ignoresSafeArea()
}
}
Если я вручную заполню массив местоположений, как это
@Published var locations: [Location] = [
Location(name: "Test", coordinate: CLLocationCoordinate2D(latitude: 43.6541, longitude: -79.3780))
]
Маркер правильно отображается на карте.
Это представление, которое начинает получать местоположения из Firestore.
struct LocationView: View {
let places = LocationsViewModel()
var body: some View {
MapView()
.ignoresSafeArea()
Spacer()
HStack(alignment: .center) {
Button {
places.getLocationsOnButton()
} label: {
Image(systemName: "location")
}
}
}
}
Я отредактировал исходный пост.
Вы никогда не вызываете функцию получения местоположения.
MapView объединяется с другим View, который вызывает getLocationsOnButton(). Я опубликую это через мгновение.
вам больше повезет, удалив объекты модели представления, в SwiftUI структура представления уже является моделью представления, и она различает эти структуры для автоматического создания/обновления/удаления объектов UIView для вас.
Комментарии здесь сработали для меня, поэтому я отмечу это как ответ.
В LocationView
у вас должно быть @StateObject var places = LocationsViewModel()
.
В MapView
вы должны иметь @ObservedObject var locationsViewModel: LocationsViewModel
и передать LocationsViewModel
ему из LocationView
, используя: MapView(locationsViewModel: places)
Вам нужно показать, как вы используете это в своем
View