I am new to SwiftUI and Realm (using flexible sync), please excuse if it sounds like an elementary question
I have location data saved in a MongoDB Atlas collection - Centre
class Centre: Object, ObjectKeyIdentifiable {
#Persisted var _id: ObjectId = ObjectId.generate()
#Persisted var centreName = ""
#Persisted var centreDesc = ""
#Persisted var centreLocation: Coordinates?
override static func primaryKey() -> String? {
return "_id"
}
convenience init(centreName: String, centreDesc: String, centreLocation: Coordinates) {
self.init()
self.centreName = centreName
self.centreDesc = centreDesc
self.centreLocation = centreLocation
}
}
and Coordinates are embedded objects where "x" is longitude and "y" is latitude
class Coordinates: EmbeddedObject, ObjectKeyIdentifiable {
#Persisted var x: Double?
#Persisted var y: Double?
}
I have created a Struct to conform to the requirements of MapAnnotation protocol as -
struct CustomAnnots: Identifiable {
let id: UUID
var nameCentreLoc: String
var descCentreLoc: String
let latitude: Double
let longitude: Double
var coordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
}
}
I am trying to populate this struct from the data from Atlas collection
My LocationView - not working
import SwiftUI
import MapKit
import RealmSwift
struct LocationView: View {
#Environment(\.realm) var realm
#ObservedResults(Centre.self) var centres
#ObservedRealmObject var centre: Centre
#State private var nameCentreLoc = ""
#State private var descCentreLoc = ""
#State private var latitude = 0.0
#State private var longitude = 0.0
#State private var annots = []
#State private var region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 24.681_858, longitude: 81.811_623),
span: MKCoordinateSpan(latitudeDelta: 10, longitudeDelta: 10)
)
var body: some View {
Map(coordinateRegion: $region, annotationItems: annots, annotationContent: { locations in
MapPin(coordinate: locations.coordinate)
})
.onAppear {
setSubscription()
initData()
}
}
private func setSubscription() {
let subscriptions = realm.subscriptions
subscriptions.write {
if let currentSubscription = subscriptions.first(named: "all_centres") {
currentSubscription.update(toType: Centre.self) { centre in
centre.centreName != ""
}
} else {
subscriptions.append(QuerySubscription<Centre>(name: "all_centres") { centre in
centre.centreName != ""
})
}
}
}
private func initData() {
nameCentreLoc = centre.centreName
descCentreLoc = centre.centreDesc
latitude = (centre.centreLocation?.y)!
longitude = (centre.centreLocation?.x)!
let annots = [for centre in centres {
CustomAnnots(id: UUID(), nameCentreLoc: nameCentreLoc, descCentreLoc: descCentreLoc, latitude: latitude, longitude: longitude)
}]
}
}
How do I populate the Struct with data from Centre collection?
Changed to (with no errors in Xcode)-
var body: some View {
let annots = [CustomAnnots(id: UUID(), nameCentreLoc: centre.centreName, descCentreLoc: centre.centreDesc, latitude: (centre.centreLocation?.y)!, longitude: (centre.centreLocation?.x)!)]
Map(coordinateRegion: $region, annotationItems: annots, annotationContent: { locations in
MapPin(coordinate: locations.coordinate)
})
.onAppear {
setSubscription()
}
}
now getting runtime error "Force unwrapping nil value"
with this function I am able to print out the results to console
func getLoc() {
for centre in centres {
var annots = [CustomAnnots.init(id: UUID(), nameCentreLoc: centre.centreName, descCentreLoc: centre.centreDesc, latitude: (centre.centreLocation?.y)!, longitude: (centre.centreLocation?.x)!)]
print(annots)
}
}
I get this printed out -
[ACCv5.CustomAnnots(id: 67E9DADA-0BCC-4D30-8136-8B666881E82D, nameCentreLoc: "HO", descCentreLoc: "Head Office Artemis Cardiac Care Gurgaon", latitude: 28.438694893842058, longitude: 77.10845294294181)]
[ACCv5.CustomAnnots(id: 26DC0C63-5A17-49C7-B4BF-FD3AA1ABF65E, nameCentreLoc: "Panipat", descCentreLoc: "Artemis Heart Centre at Ravindra Hospital", latitude: 29.388306713854682, longitude: 76.95889693063663)]
[ACCv5.CustomAnnots(id: D3A70E58-6B65-4F5D-A398-3394B7FB04DF, nameCentreLoc: "Ranchi", descCentreLoc: "Artemis Heart Centre at Raj Hospital", latitude: 23.35731237118492, longitude: 85.32288933068195)]
But I am unable to display MapAnnotations with this -
#Environment(\.realm) var realm
#ObservedResults(Centre.self) var centres
#State public var annots: [CustomAnnots]
#State private var region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 24.681_858, longitude: 81.811_623),
span: MKCoordinateSpan(latitudeDelta: 10, longitudeDelta: 10)
)
var body: some View {
ForEach (centres) { centre in
var annots = [CustomAnnots.init(id: UUID(), nameCentreLoc: centre.centreName, descCentreLoc: centre.centreDesc, latitude: (centre.centreLocation?.y)!, longitude: (centre.centreLocation?.x)!)]
}
// Text("\(annots.count)")
Map(coordinateRegion: $region, annotationItems: annots, annotationContent: { locations in
MapMarker(coordinate: locations.coordinate)
})
Final code after Jay's suggestions
class Centre: Object, ObjectKeyIdentifiable {
#Persisted var _id: ObjectId = ObjectId.generate()
#Persisted var centreName = ""
#Persisted var centreDesc = ""
#Persisted var centreLocation: Coordinates?
override static func primaryKey() -> String? {
return "_id"
}
convenience init(centreName: String, centreDesc: String, x: Double, y: Double) {
self.init()
self.centreName = centreName
self.centreDesc = centreDesc
self.centreLocation?.x = x
self.centreLocation?.y = y
}
var coordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(latitude: (centreLocation?.y)!, longitude: (centreLocation?.x)!)
}
}
Map(coordinateRegion: $region, annotationItems: centres, annotationContent: { centre in
MapMarker(coordinate: centre.coordinate)
})
Simplifying may produce a more streamlined solution.
If the objective is to populate a map from Realm objects, then I think you're on the right track but there are too many moving parts - lets reduce that code. Let me know if I misunderstood the question..
Here's some pseudo code:
Start with the Realm object that contains the data. Note we don't need the primary key because we are using ObjectId's for that. This object has everything needed to track the pin on the map, it can be observed for changes and returns a calculated var containing the MapAnnotation to be used on the map itself, based on the data in the object
class Centre: Object, ObjectKeyIdentifiable {
#Persisted var _id: ObjectId = ObjectId.generate()
#Persisted var centreName = ""
#Persisted var centreDesc = ""
#Persisted var x: Double!
#Persisted var y: Double!
var mapAnno: MapAnnotation {
//build the map annotation from the above properties
return //return the map annotation
}
convenience init(centreName: String, centreDesc: String, x: Double, y: Double) {
self.init()
self.centreName = centreName
self.centreDesc = centreDesc
self.x = x
self.y = y
}
}
Then in the view - load up all of the centers and iterate over them retreiving the MapAnnotation from each
#ObservedResults(Centre.self) var centres //loads all of the centrs
var body: some View {
ForEach (centres) { centre in
let mapAnnotation = centre.mapAnno
//add the annotation to the map
Note this pattern is pretty common as well
Map(coordinateRegion: $region,
showsUserLocation: true,
annotationItems: centres) { center in
MapAnnotation(coordinate: //get coords from centre)
Related
I am trying to build a small Map app where location for user changes all the time. In general I get latitude and longitude updates all the time. And I need to display them and show the change with sliding animation, simular to Apple FindMyFriend, when it slides over map when they are moving in live.
This is my view:
struct ContentView: View {
#StateObject var request = Calls()
#State private var mapRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 56.946285, longitude: 24.105078), span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02))
var body: some View {
Map(coordinateRegion: $mapRegion, annotationItems: $request.users){ $user in
withAnimation(.linear(duration: 2.0)) {
MapAnnotation(coordinate: CLLocationCoordinate2D(latitude: user.latitude, longitude: user.longitude)){
Circle()
}
}
}
}
}
And function call in view model, whitch changes user location, the response is just incoming string from API:
func collectUsers(_ response: String){
if users.count != 0{
var data = response.components(separatedBy: "\n")
data.removeLast()
let updates = self.users.map{ user -> User in
let newData = updateUserLocation(user: user, input: data)
return User(id: user.id, name: user.name, image: user.image, latitude: Double(newData[1])!, longitude: Double(newData[2])!)
}
DispatchQueue.main.async {
self.users = updates
}
}else{
var userData = response.components(separatedBy: ";")
userData.removeLast()
let users = userData.compactMap { userString -> User? in
let userProperties = userString.components(separatedBy: ",")
var idPart = userProperties[0].components(separatedBy: " ")
if idPart.count == 2{
idPart.removeFirst()
}
guard userProperties.count == 5 else { return nil }
guard let id = Int(idPart[0]),
let latitude = Double(userProperties[3]),
let longitude = Double(userProperties[4]) else { return nil }
return User(id: id, name: userProperties[1], image: userProperties[2], latitude: latitude, longitude: longitude)
}
DispatchQueue.main.async {
self.users = users
}
}
}
And ofcourse my #Published:
class Calls: ObservableObject{
#Published var users = [User]()
When I use the MapMarker instead of MapAnnotation the error does not appier. I would use marker, but I need each user view in map to be different.
If any one stumbles with the same issue. I spent entire day to solve this, but the awnser is that in Xcode 14 it is a bug. After I installer Xcode 13.4.1 error messages disappiered.
I have these classes - User and Centre
import Foundation
import RealmSwift
class User: Object, ObjectKeyIdentifiable {
#Persisted(primaryKey: true) var _id = UUID().uuidString
#Persisted var userName = ""
#Persisted var firstName = ""
#Persisted var lastName = ""
#Persisted var userMobile = ""
#Persisted var userCentre = ""
#Persisted var userCentreId: ObjectId?
#Persisted var userPreferences: UserPreferences?
#Persisted var lastSeenAt: Date?
#Persisted var conversations = List<Conversation>()
#Persisted var presence = "On-Line"
var isProfileSet: Bool { !(userPreferences?.isEmpty ?? true) }
var presenceState: Presence {
get { return Presence(rawValue: presence) ?? .hidden }
set { presence = newValue.asString }
}
convenience init(userName: String, id: String) {
self.init()
self.userName = userName
_id = id
userPreferences = UserPreferences()
userPreferences?.displayName = userName
presence = "On-Line"
}
}
enum Presence: String {
case onLine = "On-Line"
case offLine = "Off-Line"
case hidden = "Hidden"
var asString: String {
self.rawValue
}
}
import Foundation
import RealmSwift
import MapKit
class Centre: Object, ObjectKeyIdentifiable {
#Persisted var _id: ObjectId = ObjectId.generate()
#Persisted var centreName = ""
#Persisted var centreDesc = ""
#Persisted var centreLocation: Coordinates?
override static func primaryKey() -> String? {
return "_id"
}
convenience init(centreName: String, centreDesc: String, x: Double, y: Double) {
self.init()
self.centreName = centreName
self.centreDesc = centreDesc
self.centreLocation?.x = x
self.centreLocation?.y = y
}
var coordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(latitude: (centreLocation?.y)!, longitude: (centreLocation?.x)!)
}
}
I have a view to set up user profile and the user's associated centre with a picker showing the available centres. I would like to also save the _id of the centre when the centre name is selected. How do I do that? Is it possible to pass a hidden value in Picker or should I use two Pickers - will take up screen space.
struct SetUserProfileView: View {
#AppStorage("shouldShareLocation") var shouldShareLocation = false
#Environment(\.realm) var realm
#ObservedRealmObject var user: User
#ObservedResults(Centre.self) var centres
#Binding var isPresented: Bool
#Binding var userID: String?
#State private var displayName = ""
#State private var photo: Photo?
#State private var photoAdded = false
#State private var firstName = ""
#State private var lastName = ""
#State private var userMobile = ""
#State private var selectedCentre = ""
#State public var selectedCentreId: ObjectId
var body: some View {
Form {
Section {
if let photo = photo {
AvatarButton(photo: photo) {
self.showPhotoTaker()
}
}
if photo == nil {
Button(action: { self.showPhotoTaker() }) {
Text("Add Photo")
}
}
TextField("Display Name", text: $displayName)
TextField("First Name", text: $firstName)
TextField("Last Name", text: $lastName)
TextField( "Mobile Number", text: $userMobile)
Text("Select Centre")
Picker(selection: $selectedCentre, label: Text("Select Centre")) {
Text("Nothing Selected").tag("")
ForEach(centres, id: \.self) { centre in
Text(centre.centreName).tag(centre.centreName)
}
}
.onAppear(perform: initData)
.pickerStyle(.menu)
.accentColor(.white)
I assume you want this
Text("Select Centre")
Picker(selection: $selectedCentre, label: Text("Select Centre")) {
Text("Nothing Selected").tag("")
ForEach(centres, id: \.self) { centre in
Text(centre.centreName).tag(centre.centreName)
}
}
.onChange(of: selectedCentre) { value in
// do whatever needed here // << here !!
}
I am working on a SwiftUI project and want to place a map in a view that uses coordinates stored in Firestore. Apple's example for MapKit in SwiftUI uses static latitude and longitude parameters in the #State property and then binds the property to the Map() view.
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)
}
}
What I want to do is the following but clearly this is not allowed since you cannot access other properties in another property.
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)
}
}
So my question is, is there a way to set the coordinates from a database for a Map() in SwiftUI or is the only option to use static values for latitude and longitude?
EDIT ADDED FOR MORE INFORMATION
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: &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
}
}
}
Data is being passed to BusinessAddressRowViewModel from the BusinessAddressViewModel. BusinessAddressView holds the list that creates all the rows.
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: &cancellables)
}
}
You have an initialization problem here, having nothing to do with the Map(). You are trying to use businessCoordinates the instantiated ObservedObject variable in the initializer, and, I am sure, are getting a Cannot use instance member 'businessCoordinates' within property initializer; property initializers run before 'self' is available error.
If you don't need 'businessCoordinates' anywhere in the view, other than the data, I would recommend this:
class BusinessCoordinates: ObservableObject {
public static let shared = BusinessCoordinates()
...
}
This will give you a Singleton you can use at will. Then you use it like this:
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)
}
}
My goal is clustering annotation on map with show number of items in cluster, I have no experience in UIKit and try to avoid it. Is it possible to do it using swiftUI only? If not how to reduce intervention of UIKit?
This is how it should look like
import SwiftUI
import MapKit
struct ContentView: View {
#State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 43.64422936785126, longitude: 142.39329541313924),
span: MKCoordinateSpan(latitudeDelta: 1.5, longitudeDelta: 2)
)
var body: some View {
Map(coordinateRegion: $region, annotationItems: data) { annotation in
MapAnnotation(coordinate: annotation.coordinate) {
Image(systemName: "person.circle.fill")
.resizable()
.frame(width: 20, height: 20)
.foregroundColor(Color.purple)
}
}
.edgesIgnoringSafeArea(.all)
}
}
struct SampleData: Identifiable {
var id = UUID()
var latitude: Double
var longitude: Double
var coordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(
latitude: latitude,
longitude: longitude)
}
}
var data = [
SampleData(latitude: 43.70564024126748, longitude: 142.37968945214223),
SampleData(latitude: 43.81257464206404, longitude: 142.82112322464369),
SampleData(latitude: 43.38416585162576, longitude: 141.7252598737476),
SampleData(latitude: 45.29168643283501, longitude: 141.95286751470724),
SampleData(latitude: 45.49261392585982, longitude: 141.9343973160499),
SampleData(latitude: 44.69825427301145, longitude: 141.91227845284203)
]
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I find the way to cluster annotations with MapKit, but reuse map like a view for easy swiftUI. Looks like that https://i.stack.imgur.com/u3hKR.jpg
import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
var forDisplay = data
#State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 43.64422936785126, longitude: 142.39329541313924),
span: MKCoordinateSpan(latitudeDelta: 1.5, longitudeDelta: 2)
)
class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
}
/// showing annotation on the map
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard let annotation = annotation as? LandmarkAnnotation else { return nil }
return AnnotationView(annotation: annotation, reuseIdentifier: AnnotationView.ReuseID)
}
}
func makeCoordinator() -> Coordinator {
MapView.Coordinator(self)
}
func makeUIView(context: Context) -> MKMapView {
/// creating a map
let view = MKMapView()
/// connecting delegate with the map
view.delegate = context.coordinator
view.setRegion(region, animated: false)
view.mapType = .standard
for points in forDisplay {
let annotation = LandmarkAnnotation(coordinate: points.coordinate)
view.addAnnotation(annotation)
}
return view
}
func updateUIView(_ uiView: MKMapView, context: Context) {
}
}
struct SampleData: Identifiable {
var id = UUID()
var latitude: Double
var longitude: Double
var coordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(
latitude: latitude,
longitude: longitude)
}
}
var data = [
SampleData(latitude: 43.70564024126748, longitude: 142.37968945214223),
SampleData(latitude: 43.81257464206404, longitude: 142.82112322464369),
SampleData(latitude: 43.38416585162576, longitude: 141.7252598737476),
SampleData(latitude: 45.29168643283501, longitude: 141.95286751470724),
SampleData(latitude: 45.49261392585982, longitude: 141.9343973160499),
SampleData(latitude: 44.69825427301145, longitude: 141.91227845284203)
]
class LandmarkAnnotation: NSObject, MKAnnotation {
let coordinate: CLLocationCoordinate2D
init(
coordinate: CLLocationCoordinate2D
) {
self.coordinate = coordinate
super.init()
}
}
/// here posible to customize annotation view
let clusterID = "clustering"
class AnnotationView: MKMarkerAnnotationView {
static let ReuseID = "cultureAnnotation"
/// setting the key for clustering annotations
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
clusteringIdentifier = clusterID
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepareForDisplay() {
super.prepareForDisplay()
displayPriority = .defaultLow
}
}
And use that map like a default view
import SwiftUI
struct ContentView: View {
var body: some View {
MapView()
.edgesIgnoringSafeArea(.all)
}
}
For solving a problem I used next resources:
https://www.hackingwithswift.com/books/ios-swiftui/communicating-with-a-mapkit-coordinator
https://www.hackingwithswift.com/books/ios-swiftui/advanced-mkmapview-with-swiftui
https://developer.apple.com/videos/play/wwdc2017/237/
https://www.youtube.com/watch?v=QuYA7gQjTt4
I am trying to display a map pin on a map. Upon entry of a transaction the details are saved along with the location coordinates. In a list of transaction entries, the user may click on an entry for more detail information including a small map showing the transaction location.
Based on Asperi's suggestions at adding a MapMarker to MapKit in swiftUI 2 it appears that I need to declare an identifiable structure in order to use a map pin.
In the DetailView the latitude and longitude are copied to a coordinate parameter before transmission to MapView.
struct DetailView: View {
var coordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(
latitude: item.entryLat,
longitude: item.entryLong)
}
var body: some View {
VStack {
MapView(coordinate: coordinate)
.ignoresSafeArea(edges: .all)
.frame(height: 400)
.padding(.vertical, 10)
}
}
}
MapView is where I'm having trouble. I'm not sure how to pass in my coordinates for the region and the marker (xxxxx). Copying` coordinate to the #State region and the marker produces the error "Argument passed to call that takes no arguments".
struct Marker: Identifiable {
let id = UUID()
var location: MapMarker
}
struct MapView: View {
var coordinate: CLLocationCoordinate2D
#State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(xxxxxxx), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
let markers = [Marker(location: MapMarker(coordinate: CLLocationCoordinate2D(xxxxxxx), tint: .red))]
var body: some View {
Map(coordinateRegion: $region, showsUserLocation: true,
annotationItems: markers) { marker in
marker.location
}.edgesIgnoringSafeArea(.all)
}
}
Sounds like for your application, declaring the region as a constant will work. The code would look like this:
struct Marker: Identifiable {
let id = UUID()
var location: MapMarker
}
struct MapView: View {
var coordinate: CLLocationCoordinate2D
var body: some View {
Map(coordinateRegion: .constant(MKCoordinateRegion(center: coordinate,
span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))),
showsUserLocation: true,
annotationItems: [Marker(location: MapMarker(coordinate: coordinate))]) { marker in
marker.location
}.edgesIgnoringSafeArea(.all)
}
}
If you still wanted to use it as a #State variable, you could use a custom init to set the value:
struct MapView: View {
var coordinate: CLLocationCoordinate2D
#State private var region : MKCoordinateRegion
init(coordinate : CLLocationCoordinate2D) {
self.coordinate = coordinate
_region = State(initialValue: MKCoordinateRegion(center: coordinate,
span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)))
}
var body: some View {
Map(coordinateRegion: $region,
showsUserLocation: true,
annotationItems: [Marker(location: MapMarker(coordinate: coordinate))]) { marker in
marker.location
}
.edgesIgnoringSafeArea(.all)
}
}
Lastly, I'm defining the markers array inline, but you could split it out into a computed property:
var markers : [Marker] {
[Marker(location: MapMarker(coordinate: coordinate))]
}
var body: some View {
Map(coordinateRegion: $region,
showsUserLocation: true,
annotationItems: markers) { marker in
marker.location
}
.edgesIgnoringSafeArea(.all)
}