#swiftui #mapkit
Вопрос:
Я работаю над проектом SwiftUI и хочу разместить карту в представлении, использующем координаты, хранящиеся в Firestore. Пример Apple для MapKit в SwiftUI использует статические параметры широты и долготы в свойстве @State, а затем привязывает свойство к представлению Map ().
struct BusinessMapView: View {
@State private var region: MKCoordinateRegion = {
var mapCoordinates = CLLocationCoordinate2D(latitude: 44.621754, longitude: -66.475873)
var mapZoomLevel = MKCoordinateSpan(latitudeDelta: 5.00, longitudeDelta: 5.00)
var mapRegion = MKCoordinateRegion(center: mapCoordinates, span: mapZoomLevel)
return mapRegion
}()
var body: some View {
Map(coordinateRegion: $region)
}
}
Я хочу сделать следующее, но очевидно, что это недопустимо, так как вы не можете получить доступ к другим свойствам в другом свойстве.
struct BusinessMapView: View {
@ObservedObject var businessAddressRowViewModel: BusinessAddressRowViewModel
@State private var region: MKCoordinateRegion = {
var mapCoordinates = CLLocationCoordinate2D(latitude: businessAddressRowViewModel.businessAddress.latitude, longitude: businessAddressRowViewModel.businessAddress.longitude)
var mapZoomLevel = MKCoordinateSpan(latitudeDelta: 5.00, longitudeDelta: 5.00)
var mapRegion = MKCoordinateRegion(center: mapCoordinates, span: mapZoomLevel)
return mapRegion
}()
var body: some View {
Map(coordinateRegion: $region)
}
}
Итак, мой вопрос в том, есть ли способ задать координаты из базы данных для карты() в SwiftUI или это единственный вариант использовать статические значения широты и долготы?
ПРАВКА ДОБАВЛЕНА ДЛЯ ПОЛУЧЕНИЯ ДОПОЛНИТЕЛЬНОЙ ИНФОРМАЦИИ
class BusinessAddressRowViewModel: ObservableObject, Identifiable {
// Properties
var id: String = ""
public static let shared = BusinessAddressRowViewModel()
// Published Properties
@Published var businessAddress: BusinessAddress
// Combine Cancellable
private var cancellables = Set<AnyCancellable>()
// Initializer
init(businessAddress: BusinessAddress) {
self.businessAddress = businessAddress
self.startCombine()
}
// Starting Combine
func startCombine() {
// Get Bank Account
$businessAddress
.receive(on: RunLoop.main)
.compactMap { businessAddress in
businessAddress.id
}
.assign(to: .id, on: self)
.store(in: amp;cancellables)
}
}
The shared property gives an error stating the parameter businessAddress is missing.
The data is coming from Firebase Firestore here.
class BusinessAddressRepository: ObservableObject {
let db = Firestore.firestore()
private var snapshotListener: ListenerRegistration?
@Published var businessAddresses = [BusinessAddress]()
init() {
startSnapshotListener()
}
func startSnapshotListener() {
// Get the currentUserUid
guard let currentUserId = Auth.auth().currentUser else {
return
}
if snapshotListener == nil {
// Add a SnapshotListener to the BusinessAddress Collection.
self.snapshotListener = db.collection(FirestoreCollection.users).document(currentUserId.uid).collection(FirestoreCollection.businessAddresses).addSnapshotListener { (querySnapshot, error) in
// Check to see if an error occured and print it. IMPLEMENT ERROR HANDLING LATER
if let error = error {
print("Error getting documents: (error)")
} else {
print("BusinessAddressRepository - snapshotListener called")
// Check to make sure the Collection contains Documents
guard let documents = querySnapshot?.documents else {
print("No Business Addresses.")
return
}
// Documents exist.
self.businessAddresses = documents.compactMap { businessAddress in
do {
return try businessAddress.data(as: BusinessAddress.self)
} catch {
print(error)
}
return nil
}
}
}
}
}
func stopSnapshotListener() {
if snapshotListener != nil {
snapshotListener?.remove()
snapshotListener = nil
}
}
}
Данные передаются в BusinessAddressRowViewModel из модели BusinessAddressViewModel. BusinessAddressView содержит список, в котором создаются все строки.
class BusinessAddressViewModel: ObservableObject {
var businessAddressRepository: BusinessAddressRepository
// Published Properties
@Published var businessAddressRowViewModels = [BusinessAddressRowViewModel]()
// Combine Cancellable
private var cancellables = Set<AnyCancellable>()
// Intitalizer
init(businessAddressRepository: BusinessAddressRepository) {
self.businessAddressRepository = businessAddressRepository
self.startCombine()
}
// Starting Combine - Filter results for business addresses created by the current user only.
func startCombine() {
businessAddressRepository
.$businessAddresses
.receive(on: RunLoop.main)
.map { businessAddress in
businessAddress
.map { businessAddress in
BusinessAddressRowViewModel(businessAddress: businessAddress)
}
}
.assign(to: .businessAddressRowViewModels, on: self)
.store(in: amp;cancellables)
}
}
Комментарии:
1. Приведенный выше код во втором блоке не компилируется. Это выдает ошибку. Поскольку регион ссылается на бизнес-координаты, это запрещено. Мой вопрос в том, как вы можете использовать регион с данными из базы данных, в которой вы не можете ссылаться на другие свойства, такие как businessCoordinates.
Ответ №1:
У вас здесь проблема с инициализацией, не имеющая ничего общего с картой(). Вы пытаетесь использовать businessCoordinates
созданную ObservedObject
переменную в инициализаторе и, я уверен, получаете Cannot use instance member 'businessCoordinates' within property initializer; property initializers run before 'self' is available
ошибку.
Если вам не нужны «бизнес-координаторы» в любом месте представления, кроме данных, я бы рекомендовал следующее:
class BusinessCoordinates: ObservableObject {
public static let shared = BusinessCoordinates()
...
}
Это даст вам Singleton
то, что вы можете использовать по своему желанию. Тогда ты используешь его вот так:
struct BusinessMapView: View {
@State private var region: MKCoordinateRegion
init() {
let mapCoordinates = CLLocationCoordinate2D(latitude: BusinessCoordinates.shared.latitude, longitude: BusinessCoordinates.shared.longitude)
var mapZoomLevel = MKCoordinateSpan(latitudeDelta: 5.00, longitudeDelta: 5.00)
_region = State(initialValue: MKCoordinateRegion(center: mapCoordinates, span: mapZoomLevel))
}
var body: some View {
Map(coordinateRegion: $region)
}
}
Комментарии:
1. Хотя мне нравится этот подход, я не уверен, что он будет работать для моего приложения. Я обновил вопрос выше, чтобы показать текущий файл BusinessCoordinates. Поскольку файл является моделью представления для представления строк, я передаю виртуальной машине бизнес-адрес. Это означает, что я должен инициировать совместное свойство с деловым адресом. Я не знаю, как это было бы возможно.
2. Вы вставляете его
BusinessAddressRowViewModel
, но вMapView
то, что вы используете вBusinessCoordinates
качествеObservedObject
. Откуда это взялось?3. Прошу прощения за путаницу. Я переименовал файл в исходном сообщении, пытаясь упростить ситуацию. Я изменил имя объекта ObservedObject, чтобы оно соответствовало имени файла.
4. Откуда поступают данные? Оставляя в стороне ваш вид карты, как вы вводите данные
BusinessAddressRowViewModel
? И что хранит эти данные до того, как вы создадите экземплярBusinessAddressRowViewModel
?5. Я добавил файл репозитория, который извлекает данные из Firebase Firestore, и модель businessaddressviewмодель, которая создает модели BusinessAddressRowViewМодели.