问题
I am new in swift , I implemented a MapKit with static data , and it worked fine , and I called after that backend pins data and it showed in the playground that it works fine , but the map is not displaying the markers , it seems that the mapKit do not capture the pins data in the right time , so I used Dispatch.Que to refresh the map but I did not refresh and it is displaying without the markers
here what I have tried :
import UIKit
import MapKit
class myMapViewController: UIViewController, MKMapViewDelegate {
var shops = [Shops]()
var communities = [Community]()
var cyclists = [Cyclist]()
var circuits = [Circuit]()
var BR = BaseUrl.baseUrl
@IBOutlet weak var myMap: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
self.getShops()
self.getCircuits()
self.getCyclists()
self.getCommunities()
//shops.append(Shops(id: 0, title: "Shop1", latitude: 36.553015 , longitude: 10.592774))
//shops.append(Shops(id: 0, title: "Shop2", latitude: 35.499414 , longitude: 10.824846))
//communities.append(Community(id: 0, title: "community1", latitude: 37.276943 , longitude: 10.934709 ))
//communities.append(Community(id: 0, title: "community2", latitude: 35.427828 , longitude: 9.748186 ))
//circuits.append(Circuit(id: 0, title: "circuit1", latitude: 33.773035 , longitude: 10.857805 ))
//cyclists.append(Cyclist(id: 0, title: "cyclist1", latitude: 35.785118 , longitude: 10.000871 ))
createShopsAnnotations(locations: shops)
createCircuitsAnnotations(locations: circuits)
createCommunityAnnotations(locations: communities)
createCyclistsAnnotations(locations: cyclists)
}
func createShopsAnnotations(locations:[Shops]){
for location in locations {
let annotations = MKPointAnnotation()
annotations.title = location.title as? String
annotations.coordinate = CLLocationCoordinate2D(latitude: location.latitude as! CLLocationDegrees , longitude: location.longitude as! CLLocationDegrees)
DispatchQueue.main.async {
self.myMap.addAnnotation(annotations)
}
}}
func createCircuitsAnnotations(locations:[Circuit]){
for location in locations {
let annotations = MKPointAnnotation()
annotations.title = location.title as? String
annotations.coordinate = CLLocationCoordinate2D(latitude: location.latitude as! CLLocationDegrees , longitude: location.longitude as! CLLocationDegrees)
DispatchQueue.main.async {
self.myMap.addAnnotation(annotations)
}
}
}
func createCommunityAnnotations(locations:[Community]){
for location in locations {
let annotations = MKPointAnnotation()
annotations.title = location.title as? String
annotations.coordinate = CLLocationCoordinate2D(latitude: location.latitude as! CLLocationDegrees , longitude: location.longitude as! CLLocationDegrees)
DispatchQueue.main.async {
self.myMap.addAnnotation(annotations)
}
}}
func createCyclistsAnnotations(locations:[Cyclist]){
for location in locations {
let annotations = MKPointAnnotation()
annotations.title = location.title as? String
annotations.coordinate = CLLocationCoordinate2D(latitude: location.latitude as! CLLocationDegrees , longitude: location.longitude as! CLLocationDegrees)
DispatchQueue.main.async {
self.myMap.addAnnotation(annotations)
}
}}
func getShops(){
//get
guard let url = URL(string: BR+"/shops") else {
return
}
let session = URLSession.shared
session.dataTask(with: url) { ( data , response ,error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do
{
let json = try JSONSerialization.jsonObject(with: data, options: [])as! [[String:Any]]
self.shops.removeAll()
for item in json {
let id = item["shop_id"] as! Int
let title = item["title"] as! String
let latitude = item["latitude"] as! Double
let longitude = item["longitude"] as! Double
self.shops.append(Shops(id: id, title: title, latitude: latitude , longitude: longitude))
}
for item in self.shops {
print(item.shop_id)
print(item.title)
print(item.latitude)
print(item.longitude)
}
}catch{
print(error)
}
}
}.resume()
}
func getCommunities(){
//get
guard let url = URL(string: BR+"/communities") else {
return
}
let session = URLSession.shared
session.dataTask(with: url) { ( data , response ,error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do
{
let json = try JSONSerialization.jsonObject(with: data, options: [])as! [[String:Any]]
self.communities.removeAll()
for item in json {
let id = item["community_id"] as! Int
let title = item["title"] as! String
let latitude = item["latitude"] as! Double
let longitude = item["longitude"] as! Double
self.communities.append(Community(id: id, title: title, latitude: latitude , longitude: longitude))
}
for item in self.communities {
print(item.community_id)
print(item.title)
print(item.latitude)
print(item.longitude)
}
}catch{
print(error)
}
}
}.resume()
}
func getCircuits(){
//get
guard let url = URL(string: BR+"/circuits") else {
return
}
let session = URLSession.shared
session.dataTask(with: url) { ( data , response ,error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do
{
let json = try JSONSerialization.jsonObject(with: data, options: [])as! [[String:Any]]
self.shops.removeAll()
for item in json {
let id = item["circuit_id"] as! Int
let title = item["title"] as! String
let latitude = item["latitude"] as! Double
let longitude = item["longitude"] as! Double
self.circuits.append(Circuit(id: id, title: title, latitude: latitude , longitude: longitude))
}
for item in self.circuits {
print(item.circuit_id)
print(item.title)
print(item.latitude)
print(item.longitude)
}
}catch{
print(error)
}
}
}.resume()
}
func getCyclists(){
//get
guard let url = URL(string: BR+"/cyclists") else {
return
}
let session = URLSession.shared
session.dataTask(with: url) { ( data , response ,error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do
{
let json = try JSONSerialization.jsonObject(with: data, options: [])as! [[String:Any]]
self.cyclists.removeAll()
for item in json {
let id = item["cyclist_id"] as! Int
let title = item["title"] as! String
let latitude = item["latitude"] as! Double
let longitude = item["longitude"] as! Double
self.cyclists.append(Cyclist(id: id, title: title, latitude: latitude , longitude: longitude))
}
for item in self.cyclists {
print(item.cyclist_id)
print(item.title)
print(item.latitude)
print(item.longitude)
}
}catch{
print(error)
}
}
}.resume()
}
}
What I am trying to do is to make the mapkit capture the pins data in the right time and refresh it's data, I think it is the only way to display my pins correctly
回答1:
Getting the data is an async task. In the question you are updating the data before the network request has ended. Combining this question with this one Set MapKit pins with different colors and the answer by Rob you could assign each URL path to the place type:
enum PlaceType {
case shop, community, cyclist, circuit
var urlPath: String {
switch self {
case .shop: return "/shops"
case .community: return "/communities"
case .circuit: return "/circuits"
case .cyclist: return "/cyclists"
}
}
and then you could fetch the data in a for loop for each place type. Each time you call the method to fetch data, you'd pass a callback to add the returned places to the map:
@IBOutlet weak var myMap: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
for placeType in PlaceType.allCases {
// Call each get method with a callback
fetchData(
for: placeType,
completion: { [weak self] places in
guard let self = self else { return }
self.myMap.addAnnotations(places)
}
)
}
}
The fetchData
method just starts the network request and calls the completion method when done:
func fetchData(for placeType: PlaceType, completion: @escaping ([Place]) -> Void) {
guard let url = URL(string: BR + placeType.urlPath) else { return }
URLSession.shared.dataTask(with: url) { ( data , response, error) in
guard let data = data else { return }
do {
let places = try JSONDecoder().decode([Place].self, from: data)
completion(places) // this is the callback!
} catch {}
}.resume()
}
The Place
class would be the same one as in the answer from Rob, but with decoding and encoding logic:
class Place: NSObject, MKAnnotation, Codable {
let id: Int
let type: PlaceType
let latitude: Double
let longitude: Double
dynamic var title: String?
lazy var coordinate: CLLocationCoordinate2D = {
CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
}()
init(id: Int, title: String, latitude: Double, longitude: Double, type: PlaceType) {
self.id = id
self.type = type
self.title = title
self.latitude = latitude
self.longitude = longitude
super.init()
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Try to decode each place type id to see which place type the data is for
var type: PlaceType? = nil
var tempID: Int? = nil
PlaceType.allCases.forEach { placeType in
if let id = try? container.decode(Int.self, forKey: placeType.codingKey) {
type = placeType
tempID = id
}
}
guard let decodedType = type, let decodedID = tempID else {
let context = DecodingError.Context(
codingPath: decoder.codingPath,
debugDescription: "Data does not have data for any of existing place types"
)
throw DecodingError.valueNotFound(Place.self, context)
}
self.type = decodedType
self.id = decodedID
self.title = try container.decode(String.self, forKey: .title)
self.latitude = try container.decode(Double.self, forKey: .latitude)
self.longitude = try container.decode(Double.self, forKey: .longitude)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: type.codingKey)
try container.encode(title, forKey: .title)
try container.encode(latitude, forKey: .latitude)
try container.encode(longitude, forKey: .longitude)
}
enum CodingKeys: String, CodingKey {
case shop_id, community_id, circuit_id, cyclist_id
case title, latitude, longitude
}
}
You can see the complete working code here. Feel free to ask questions in Github about the code if you do not understand something.
回答2:
import UIKit
import MapKit
class myMapViewController: UIViewController, MKMapViewDelegate {
var shops = [Shops]()
var communities = [Community]()
var cyclists = [Cyclist]()
var circuits = [Circuit]()
var BR = BaseUrl.baseUrl
@IBOutlet weak var myMap: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
self.myMap.delegate = self
self.getCircuits()
self.getCyclists()
self.getCommunities()
//shops.append(Shops(id: 0, title: "Shop1", latitude: 36.553015 , longitude: 10.592774))
//shops.append(Shops(id: 0, title: "Shop2", latitude: 35.499414 , longitude: 10.824846))
//communities.append(Community(id: 0, title: "community1", latitude: 37.276943 , longitude: 10.934709 ))
//communities.append(Community(id: 0, title: "community2", latitude: 35.427828 , longitude: 9.748186 ))
//circuits.append(Circuit(id: 0, title: "circuit1", latitude: 33.773035 , longitude: 10.857805 ))
//cyclists.append(Cyclist(id: 0, title: "cyclist1", latitude: 35.785118 , longitude: 10.000871 ))
createShopsAnnotations(locations: shops)
createCircuitsAnnotations(locations: circuits)
createCommunityAnnotations(locations: communities)
createCyclistsAnnotations(locations: cyclists)
}
func createShopsAnnotations(locations:[Shops]){
for location in locations {
let annotations = MKPointAnnotation()
annotations.title = location.title as? String
annotations.coordinate = CLLocationCoordinate2D(latitude: location.latitude as! CLLocationDegrees , longitude: location.longitude as! CLLocationDegrees)
DispatchQueue.main.async {
self.myMap.addAnnotation(annotations)
}
}}
func createCircuitsAnnotations(locations:[Circuit]){
for location in locations {
let annotations = MKPointAnnotation()
annotations.title = location.title as? String
annotations.coordinate = CLLocationCoordinate2D(latitude: location.latitude as! CLLocationDegrees , longitude: location.longitude as! CLLocationDegrees)
DispatchQueue.main.async {
self.myMap.addAnnotation(annotations)
}
}
}
func createCommunityAnnotations(locations:[Community]){
for location in locations {
let annotations = MKPointAnnotation()
annotations.title = location.title as? String
annotations.coordinate = CLLocationCoordinate2D(latitude: location.latitude as! CLLocationDegrees , longitude: location.longitude as! CLLocationDegrees)
DispatchQueue.main.async {
self.myMap.addAnnotation(annotations)
}
}}
func createCyclistsAnnotations(locations:[Cyclist]){
for location in locations {
let annotations = MKPointAnnotation()
annotations.title = location.title as? String
annotations.coordinate = CLLocationCoordinate2D(latitude: location.latitude as! CLLocationDegrees , longitude: location.longitude as! CLLocationDegrees)
DispatchQueue.main.async {
self.myMap.addAnnotation(annotations)
}
}}
func getShops(){
//get
guard let url = URL(string: BR+"/shops") else {
return
}
let session = URLSession.shared
session.dataTask(with: url) { ( data , response ,error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do
{
let json = try JSONSerialization.jsonObject(with: data, options: [])as! [[String:Any]]
self.shops.removeAll()
for item in json {
let id = item["shop_id"] as! Int
let title = item["title"] as! String
let latitude = item["latitude"] as! Double
let longitude = item["longitude"] as! Double
self.shops.append(Shops(id: id, title: title, latitude: latitude , longitude: longitude))
}
for item in self.shops {
print(item.shop_id)
print(item.title)
print(item.latitude)
print(item.longitude)
}
self.createShopsAnnotations(locations: self.shops)
}catch{
print(error)
}
}
}.resume()
}
func getCommunities(){
//get
guard let url = URL(string: BR+"/communities") else {
return
}
let session = URLSession.shared
session.dataTask(with: url) { ( data , response ,error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do
{
let json = try JSONSerialization.jsonObject(with: data, options: [])as! [[String:Any]]
self.communities.removeAll()
for item in json {
let id = item["community_id"] as! Int
let title = item["title"] as! String
let latitude = item["latitude"] as! Double
let longitude = item["longitude"] as! Double
self.communities.append(Community(id: id, title: title, latitude: latitude , longitude: longitude))
}
for item in self.communities {
print(item.community_id)
print(item.title)
print(item.latitude)
print(item.longitude)
}
self.createCommunityAnnotations(locations: self.communities)
}catch{
print(error)
}
}
}.resume()
}
func getCircuits(){
//get
guard let url = URL(string: BR+"/circuits") else {
return
}
let session = URLSession.shared
session.dataTask(with: url) { ( data , response ,error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do
{
let json = try JSONSerialization.jsonObject(with: data, options: [])as! [[String:Any]]
self.shops.removeAll()
for item in json {
let id = item["circuit_id"] as! Int
let title = item["title"] as! String
let latitude = item["latitude"] as! Double
let longitude = item["longitude"] as! Double
self.circuits.append(Circuit(id: id, title: title, latitude: latitude , longitude: longitude))
}
for item in self.circuits {
print(item.circuit_id)
print(item.title)
print(item.latitude)
print(item.longitude)
}
self.createCircuitsAnnotations(locations: self.circuits)
}catch{
print(error)
}
}
}.resume()
}
func getCyclists(){
//get
guard let url = URL(string: BR+"/cyclists") else {
return
}
let session = URLSession.shared
session.dataTask(with: url) { ( data , response ,error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do
{
let json = try JSONSerialization.jsonObject(with: data, options: [])as! [[String:Any]]
self.cyclists.removeAll()
for item in json {
let id = item["cyclist_id"] as! Int
let title = item["title"] as! String
let latitude = item["latitude"] as! Double
let longitude = item["longitude"] as! Double
self.cyclists.append(Cyclist(id: id, title: title, latitude: latitude , longitude: longitude))
}
for item in self.cyclists {
print(item.cyclist_id)
print(item.title)
print(item.latitude)
print(item.longitude)
}
self.createCyclistsAnnotations(locations: self.cyclists)
}catch{
print(error)
}
}
}.resume()
}
}
来源:https://stackoverflow.com/questions/65647465/reload-map-after-calling-urlsession-data