I have already decode my JSON API and successfully display the location on the map with MapAnnotations
and put NavigationLink
to see the detail on it. But somehow, when I zoomed out the map to see all marked locations, suddenly my view becomes very laggy with simulator and real iPhone 8 (maybe because I have 100+ annotations on the map?). And then I tried to use MapMarker
and the view becomes more smoother, but the problem is now I can't put NavigationLink
on MapMarker
as well as MapPin
. Is there a proper way to display marker/annotations on the map and NavigationLink
without making the view lag??
Here is my LocationManager Code to track user's location
import Foundation
import CoreLocation
class LocationManager: NSObject, ObservableObject {
private let locationManager = CLLocationManager()
@Published var location: CLLocation?
override init() {
super.init()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
locationManager.delegate = self
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
locationManager.stopUpdatingLocation()
DispatchQueue.main.async {
self.location = location
}
}
}
My ContentView to display the Map and Show the annotations
import SwiftUI
import MapKit
import Combine
struct ContentView: View {
var body: some View {
NavigationView{
ServiceLocation()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
extension MKCoordinateRegion {
static var defaultRegion: MKCoordinateRegion {
MKCoordinateRegion(center: CLLocationCoordinate2D.init(latitude: -0.789275, longitude: 113.921327), latitudinalMeters: 5000, longitudinalMeters: 5000)
}
}
//MARK: MAP VIEW
struct ServiceLocation: View{
@State var serviceLocations: [ServiceLocationJSON] = []
@ObservedObject private var locationManager = LocationManager()
@State private var region = MKCoordinateRegion.defaultRegion
@State private var cancellable: AnyCancellable?
private func setCurrentLocation() {
cancellable = locationManager.$location.sink { location in
region = MKCoordinateRegion(center: location?.coordinate ?? CLLocationCoordinate2D(), latitudinalMeters: 20000, longitudinalMeters: 20000)
}
}
var body: some View{
GeometryReader{ geometry in
VStack{
if locationManager.location != nil {
Map(coordinateRegion: $region, interactionModes: .all, showsUserLocation: true, userTrackingMode: .none, annotationItems: serviceLocations) { location in
MapAnnotation(coordinate: CLLocationCoordinate2D(latitude: location.LATITUDE, longitude: location.LONGITUDE)){
NavigationLink(destination: serviceLocationDetail(serviceLocations: location)){
Image(systemName: "mappin")
.resizable()
.scaledToFit()
.frame(width: geometry.size.width / 15, height: geometry.size.height / 15)
}
}
}
} else {
VStack{
Spacer()
ProgressView()
Spacer()
}
}
}.onAppear{
setCurrentLocation()
getServiceLocation(url: "https://my.api.mockaroo.com/latlong.json?key=e57d0e40"){ (serviceLocations) in
self.serviceLocations = serviceLocations
}
}
.navigationTitle("Service")
.navigationBarTitleDisplayMode(.inline)
}
}
}
//MARK: DETAIL VIEW
struct serviceLocationDetail: View{
var serviceLocations: ServiceLocationJSON
var body: some View{
VStack{
if serviceLocations.DEALER_NAME.isEmpty{
VStack{
Spacer()
ProgressView()
Spacer()
}
}else{
VStack(alignment: .leading, spacing: 10){
Text(serviceLocations.DEALER_NAME)
.fontWeight(.medium)
.padding(.leading, 10)
Text(serviceLocations.DEALER_ADDRESS)
.padding(.leading, 10)
HStack(spacing: 5){
Image(systemName: "phone.fill")
Text(serviceLocations.PHONE)
}.padding(.leading, 10)
Spacer()
}.navigationBarTitle(serviceLocations.DEALER_NAME)
}
}
Spacer()
}
}
//MARK: JSON MODEL
struct ServiceLocationJSON: Identifiable, Decodable{
var id: Int
var LATITUDE: Double
var LONGITUDE: Double
var DEALER_NAME: String
var DEALER_ADDRESS: String
var DEALER_PICTURE: String
var PHONE: String
}
//MARK: DECODE JSON MODEL
func getServiceLocation(url: String, completion: @escaping ([ServiceLocationJSON])->()){
let session = URLSession(configuration: .default)
session.dataTask(with: URL(string: url)!){ (data, _, err) in
if err != nil{
print(err!.localizedDescription)
return
}
do{
let serviceLocations = try
JSONDecoder().decode([ServiceLocationJSON].self, from: data!)
completion(serviceLocations)
}
catch{
print(error)
}
}.resume()
}
Build with Xcode 12 and Swift 5
Nevermind, I just solved it. I just need to change coordinateRegion: $region
to coordinateRegion: .constant(region)