Reputation: 33
Note: I'm a beginner to Swift and MapKit so bear with me please. I appreciate it. I have a SwiftUI view that takes an ObservableObject as input
@ObservedObject var viewModel: PostRowViewModel
And within that ObservableObject there is a @Published field:
@Published var post: Post
Now what I want to do is to display a map of using the lat and long values that are fields in this post object. Now the Map view in MapKit is as follows:
Map(coordinateRegion: Binding<MKCoordinateRegion>)
So I need to provide it with a binding of the region of the post I'm trying to display. What I tried to do is to initialize the region as follows:
@State var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: viewModel.post.location.coordinate.latitude, longitude: viewModel.post.location.coordinate.latitude), span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02))
Using the info from the viewModel. However I get this error:
Cannot use instance member 'viewModel' within property initializer; property initializers run before 'self' is available.
So I searched online and found that to solve the issue you need to use an init function however since the viewModel is given as input to the view and I have Environment variable I dont want to include an init function. I've also tried to create a function inside the viewModel that returns a Binding as follows:
func createMapRegion() -> Binding<MKCoordinateRegion> {
@State var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: post.location.coordinate.latitude, longitude: post.location.coordinate.longitude), span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02))
return $region
}
But I get the warning: Accessing State's value outside of being installed on a View. This will result in a constant Binding of the initial value and will not update. Because the state is being accessing outside since when I then create the map I do it like so:
Map(coordinateRegion: viewModel.createMapRegion())
So I'm not sure how I can access this info from the post published object using the viewModel and create a Map when the viewModel is given as input to the view.
Any help would be very much appreciated!
Code:
ViewModel:
import Foundation
import SwiftUI
import MapKit
@MainActor
@dynamicMemberLookup
class PostRowViewModel: ObservableObject, StateManager {
..
@Published var post: Post
..
init(post: Post...) {
self.post = post
...
}
subscript<T>(dynamicMember keyPath: KeyPath<Post, T>) -> T {
post[keyPath: keyPath]
}
}
View:
import SwiftUI
import MapKit
struct PostRowView: View {
.
.
.
@ObservedObject var viewModel: PostRowViewModel
.
.
var body: some View {
// I want to create a map here that uses the post stored in the viewModel.
Map(coordinateRegion: ...)
}
}
}
Where I want to access the values for lat and long using the Post struct which is:
struct Post: Identifiable, Codable, Equatable {
.
.
.
var location: LocationInfo
.
.
.
}
Where LocationInfo is:
struct LocationInfo: Codable, Equatable, Identifiable {
var name: String
var countryCode: String
var coordinate: Coordinate
var id = UUID()
}
And coordinate is:
struct Coordinate: Codable, Hashable {
let latitude, longitude: Double
}
Upvotes: 2
Views: 637
Reputation: 52565
As suggested in the comments, the path of least resistance is probably to put region
in a @Published
variable on your ObservableObject
:
class PostRowViewModel: ObservableObject {
@Published var post: Post
@Published var region: MKCoordinateRegion
init(post: Post) {
self.post = post
self.region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: post.location.coordinate.latitude, longitude: post.location.coordinate.longitude), span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02))
}
}
struct PostRowView: View {
@ObservedObject var viewModel: PostRowViewModel
var body: some View {
Map(coordinateRegion: $viewModel.region)
}
}
Note that you don't actually need a view model for this. You could also do something like:
struct PostRowView: View {
var post: Post
@State private var region : MKCoordinateRegion = .init()
var body: some View {
Map(coordinateRegion: $region)
.onAppear {
region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: post.location.coordinate.latitude, longitude: post.location.coordinate.longitude), span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02))
}
}
}
Upvotes: 1