'Adding Callout to Pin in swiftUI Map
I'm working on my first map app in swift and I'm a bit stuck. I have gotten this far but I'm not sure how to get callouts to show up when I click on the pin. I read about using .onTapGesture and can get a print out in the console when I do that, but I'm a bit lost in getting a callout to show up when clicking on the point that is on the map. Can someone point me in the right direction or even tell me if the current code I have will work with callouts?
import MapKit
import SwiftUI
let mapView = MKMapView()
struct ContentView: View {
@State private var locations: [Location] = []
@State private var coordinateRegion = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 32.605, longitude: -85.4875),
span: MKCoordinateSpan(latitudeDelta: 0.013, longitudeDelta: 0.013)
)
@State private var showTitle = true
var body: some View {
Text("Auburn Campus Tour").font(.title)
Map(
coordinateRegion: $coordinateRegion,
showsUserLocation: true, annotationItems: locations
) {location in
MapAnnotation(
coordinate: CLLocationCoordinate2D(
latitude: location.latitude,
longitude: location.longitude
)
) {
VStack {
Image("au")
.resizable()
.edgesIgnoringSafeArea(.top)
.frame(width: 35, height: 30)
Text(location.name)
.font(.system(size: 12))
.bold()
}.onTapGesture {
// AssetAnnotationView(au)
}
}
}
.onAppear(perform: readFile)
.onAppear{
MKMapView.appearance().mapType = .standard
MKMapView.appearance().showsPointsOfInterest = false
print("Annotations Loaded From File")
print("latitude: 32.60, longitude: -85.4875")
}
}
private func readFile() {
if let url = Bundle.main.url(forResource: "auburn", withExtension: "json"),
let data = try? Data(contentsOf: url) {
let decoder = JSONDecoder()
if let jsonData = try? decoder.decode(JSONData.self, from: data) {
self.locations = jsonData.locations
}
}
}
struct JSONData: Decodable {
let locations: [Location]
}
struct AssetAnnotationView: View {
var image: UIImage
var body: some View {
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.cornerRadius(3.0)
.padding(4)
}
}
}
struct Location: Decodable, Identifiable {
let id: Int
let name: String
let latitude: Double
let longitude: Double
let desc: String
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Solution 1:[1]
The easiest way is to make a view struct for your pin. Then you can just drop it into place.
This is an example of a view for the pin:
@State private var showCallout = false
let title: String
let subtitle: String?
let color: UIColor
@Binding var isSelected: Bool?
init(title: String, subtitle: String? = nil, color: UIColor, isSelected: Binding<Bool?> = .constant(nil)) {
self.title = title
self.subtitle = subtitle
self.color = color
self._isSelected = isSelected
}
var body: some View {
VStack(spacing: 0) {
VStack {
Text(title)
.font(.callout)
.padding(5)
Text(subtitle ?? "")
.font(.callout)
.padding(5)
.opacity(showCallout && (subtitle != nil) ? 1 : 0)
}
.background(Color(.white))
.cornerRadius(10)
.opacity(showCallout ? 1 : 0)
Image(systemName: "exclamationmark.circle.fill")
.font(.title)
.foregroundColor(Color(uiColor: color))
Image(systemName: "arrowtriangle.down.fill")
.font(.caption)
.foregroundColor(Color(uiColor: color))
.offset(x: 0, y: -5)
}
.onTapGesture {
showCallout.toggle()
}
.onLongPressGesture {
if let _ = isSelected {
self.isSelected!.toggle()
}
}
}
}
You would use it like this:
Map(
coordinateRegion: $coordinateRegion,
showsUserLocation: true, annotationItems: locations
) {location in
MapAnnotation(
coordinate: CLLocationCoordinate2D(
latitude: location.latitude,
longitude: location.longitude
)
) {
MapPin(title: location.name, subtitle: location.desc, color: Color.red)
}
This will show the callout when tapped, and as a bonus, provides a binding for a long press selection of the annotation.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Yrb |
