SwiftUI:如何将 NavigationLink 放入地图标记的 MapPing 中
SwiftUI: How to put NavigationLink in MapPin or MapMarker
我已经解码了我的 JSON API 并成功地在地图上用 MapAnnotations
显示了位置并输入 NavigationLink
以查看其详细信息。但是不知何故,当我缩小地图以查看所有标记的位置时,突然间我的视图在模拟器和真实 iPhone 8 中变得非常滞后(可能是因为我在地图上有 100 多个注释?)。然后我尝试使用 MapMarker
并且视图变得更平滑,但现在问题是我不能将 NavigationLink
放在 MapMarker
和 MapPin
上。有没有合适的方法在地图上显示 marker/annotations 和 NavigationLink
而不会造成视图延迟??
这是我用于跟踪用户位置的 LocationManager 代码
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
}
}
}
我的 ContentView 用于显示地图和显示注释
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()
}
构建 Xcode 12 和 Swift 5
没关系,我刚刚解决了它。我只需要将 coordinateRegion: $region
更改为 coordinateRegion: .constant(region)
我已经解码了我的 JSON API 并成功地在地图上用 MapAnnotations
显示了位置并输入 NavigationLink
以查看其详细信息。但是不知何故,当我缩小地图以查看所有标记的位置时,突然间我的视图在模拟器和真实 iPhone 8 中变得非常滞后(可能是因为我在地图上有 100 多个注释?)。然后我尝试使用 MapMarker
并且视图变得更平滑,但现在问题是我不能将 NavigationLink
放在 MapMarker
和 MapPin
上。有没有合适的方法在地图上显示 marker/annotations 和 NavigationLink
而不会造成视图延迟??
这是我用于跟踪用户位置的 LocationManager 代码
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
}
}
}
我的 ContentView 用于显示地图和显示注释
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()
}
构建 Xcode 12 和 Swift 5
没关系,我刚刚解决了它。我只需要将 coordinateRegion: $region
更改为 coordinateRegion: .constant(region)