iOS Mapkit Annotation Pin Shrinking - swift

MapView is pin skrinking everytime. I'm using MKAnnotationView. Can anyone tell what what am I missing here why the pin shrinking? I want to show the pin like that star icon which is also annotation.
Also everytime I change the current location co-cordinate it will create new pin and old one also be there.
class CustomPointAnnotation: MKPointAnnotation {
var imageName: String!
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
let annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "MyMarker")
annotationView.canShowCallout = true
switch annotation.title! {
case "Your Current Location":
annotationView.glyphImage = UIImage(named: "user_location")
default:
annotationView.canShowCallout = true
annotationView.glyphImage = UIImage(named: "user_location")
let buttonPin = UIButton(type: .custom)
buttonPin.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
buttonPin.setImage(UIImage(named: "PhoneIcon"), for: .normal)
annotationView.rightCalloutAccessoryView = buttonPin
annotationView.markerTintColor = UIColor.purple
}
return annotationView
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
mapView.removeAnnotation(annotation)
let currentLocationValue = manager.location!.coordinate
let servicelocValue: CLLocationCoordinate2D = CLLocationCoordinate2DMake(coordinates.latitude, coordinates.longitude)
let annotation1 = CustomPointAnnotation()
annotation1.title = "Your Current Location"
annotation1.coordinate = currentLocationValue
annotation1.imageName = "user_location.png"
let annotation2 = CustomPointAnnotation()
annotation2.title = service ?? ""
annotation2.subtitle = location
annotation2.coordinate = servicelocValue
annotation2.imageName = "user_location.png"
annotations.append(annotation1)
annotations.append(annotation2)
mapView.addAnnotations(annotations)
mapView.cameraZoomRange = MKMapView.CameraZoomRange(minCenterCoordinateDistance: 50, maxCenterCoordinateDistance: 2000)
mapView.selectAnnotation(annotation2, animated: false)
}
}

Related

Reload MKAnnotation in mapView - Swift

I have a problem, I have to constantly reload my view as I have to be able to view within this function the value of a UISlider that is constantly updated through an animation made through a function that manages the touch of a button. I noticed that when I open the map this slider value always remains fixed at the initial value, despite the latter having changed. How do I update the annotations? I have read on other posts about deleting and re-adding them:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
{
if (annotation is MapEarthquakeAnnotation) {
let mapEqAnnotation = annotation as! MapEarthquakeAnnotation
var view: MKPinAnnotationView
print("MARIOCASH",Int(sliderTime.value))
if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: mapEqAnnotation.identifier) as? MKPinAnnotationView
{
// Recycle old view
dequeuedView.annotation = annotation
view = dequeuedView
}
else
{
// Create new view
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: mapEqAnnotation.identifier)
view.canShowCallout = true
view.pinTintColor = UIColor.brown
let infoButton = UIButton(type: .infoDark)
infoButton.addTarget(self, action: #selector(infoButtonClicked(_:)), for: .touchUpInside)
view.rightCalloutAccessoryView = infoButton
}
return view
}else if annotation is MapSeismometerAnnotation
{
if let annotation = annotation as? MapSeismometerAnnotation
{
var view: MKPinAnnotationView
/*
// Recycle old view
if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: annotation.identifier) as? MKPinAnnotationView
{
dequeuedView.annotation = annotation
view = dequeuedView
}
else
{ */
// Create new view
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: annotation.identifier)
view.canShowCallout = true
view.pinTintColor = UIColor.green
view.image = UIImage(named: "pin-verde")
if annotation.identifier == "redpin"
{
var count = 0
var c = 0.8 //valore iniziale dell'aplha per l'opacità del pin rosso
view.image = UIImage(named: "pin-rosso")
Timer.scheduledTimer(withTimeInterval: 1,repeats: true)
{t in //ogni secondo questo timer cambia il valore dell'alpha del pin che sta vibrando
view.alpha = CGFloat(c)
count += 1
c -= 0.15
if count == 5
{
t.invalidate()
}
}
}
return view
//}
}
return nil
}
return nil
}

Weird behavior of custom annotations views

I've built the same code in SwiftUI and Storyboard and I get the same weird issues.
Whenever I run and go to background and back to foreground, the custom views are lost.
Whenever I zoom-drag far (say 10km), I also lose the non-picture-holding pin, eg "Pencil".
I'm not sure where I've gone amiss, except maybe that I'd need to separate my pins per annotation view "type" (with a given picture, with text...) and set a different reuse identifier based on that, but it sounds weird given I'm not actually creating anything new (only setting properties of the base MKAnnotationView class).
Using the UIKit version as reference:
import UIKit
import MapKit
class MapViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
let cityMapManager = CityMapManager()
let cityMapViewDelegate = CityMapViewDelegate()
let places: [Place] = [Place(name: "Cathedral", location: .init(latitude: 50.640364, longitude: 3.062058))]
override func viewDidLoad() {
super.viewDidLoad()
mapView.setRegion(MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 50.640364, longitude: 3.062058), latitudinalMeters: 500.0, longitudinalMeters: 500.0), animated: false)
mapView.showsUserLocation = true
mapView.showsScale = true
mapView.delegate = cityMapViewDelegate
annotateLocalPOI(map: mapView)
}
private func annotateLocalPOI(map: MKMapView) {
var annotation = MKPointAnnotation()
annotation.title = "Red Pin"
annotation.subtitle = "I'm a pin and I'm red. Maybe, or not."
annotation.coordinate = CLLocationCoordinate2D(latitude: 50.64, longitude: 3.06)
// add the annotation to the map (will use red pin, mapView:viewFor: allows changing the display)
map.addAnnotation(annotation)
annotation = MKPointAnnotation()
annotation.title = "Pencil"
annotation.subtitle = "Will not display"
annotation.coordinate = CLLocationCoordinate2D(latitude: 50.642, longitude: 3.059)
map.addAnnotation(annotation)
}
}
struct Place: Identifiable {
let id: UUID = UUID()
let name: String
let location: CLLocationCoordinate2D
}
class CityMapViewDelegate: NSObject, MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard annotation is MKPointAnnotation else { return nil }
let identifier = "Annotation"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView!.canShowCallout = true
if annotation.title == "Red Pin" {
let pinImage = UIImage(systemName: "timelapse")
annotationView?.image = pinImage
}
if annotation.title == "Pencil" {
// this overrules any subtitle available
annotationView?.detailCalloutAccessoryView = UIImageView(image: UIImage(systemName: "pencil"))
}
} else {
// reset
annotationView?.detailCalloutAccessoryView = nil
annotationView?.image = nil
// set it up again
annotationView?.annotation = annotation
if annotation.title == "Red Pin" {
let pinImage = UIImage(systemName: "timelapse")
annotationView?.image = pinImage
}
if annotation.title == "Pencil" {
// this overrules any subtitle available
annotationView?.detailCalloutAccessoryView = UIImageView(image: UIImage(systemName: "pencil"))
}
}
return annotationView
}
}
class CityLocationManagerDelegate: NSObject, CLLocationManagerDelegate {
var locationStatus = "Status not determined"
var locationFixFound = false
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// We've (at least once) gone under a certain minimum accuracy required to consider we have a "fix"
// we could use that to reflect that information on the UI (color, shape...)
if !locationFixFound {
for location in locations {
if location.horizontalAccuracy < 25.0 {
locationFixFound = true
}
}
}
}
func locationManager(_ manager: CLLocationManager,
didFailWithError error: Error) {
manager.stopUpdatingLocation()
print("Location manager stopped due to error: \(error)")
}
func locationManager(_ manager: CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus) {
print("Authorization Status changed")
var shouldRequestLocationUpdates = false
switch status {
case CLAuthorizationStatus.restricted:
locationStatus = "Restricted Access to location"
case CLAuthorizationStatus.denied:
locationStatus = "User denied access to location"
case CLAuthorizationStatus.notDetermined:
locationStatus = "Status not determined"
default:
locationStatus = "Allowed to location Access"
shouldRequestLocationUpdates = true
}
if (shouldRequestLocationUpdates == true) {
NSLog("Location to Allowed")
// Start location services
manager.startUpdatingLocation()
} else {
NSLog("Denied access: \(locationStatus)")
}
}
}
struct CityMapManager {
let locationDelegate: CLLocationManagerDelegate
let locationManager: CLLocationManager
init() {
locationDelegate = CityLocationManagerDelegate()
locationManager = CLLocationManager()
locationManager.delegate = locationDelegate
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.requestAlwaysAuthorization()
}
}
and a storyboard that holds a fullscreen map within a class set to MapViewController.

How to let user to add custom annotation?

I could not found any document, video or stackoverflow answer.
Here is my problem. I created map and add into my custom MKAnnotation and MKAnnotationView.
I want to let user to create custom pin and save to it's local via CoreData
MyCustomAnnotation has same attributes which is title, subtitle, and coordinate.
The first solution that I come up with put a button which creates a draggable pin to user location.
But I need to get less complex, more sophistication solution.
private func addPins() {
let list = PrivateLocations.shared.initLocations()
for pin in list {
map.addAnnotation(pin)
}
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if view.annotation is MKUserLocation { return }
let views = Bundle.main.loadNibNamed("CustomCalloutView", owner: nil, options: nil)
let customView = views?[0] as! CustomCalloutView
customView.delegate = self
customView.isUserInteractionEnabled = true
customView.titleLabel.text = view.annotation?.title!
customView.desc.text = view.annotation?.subtitle!
customView.center = CGPoint(x: view.bounds.size.width / 2, y: -customView.bounds.size.height*0.52)
view.addSubview(customView)
map.setCenter((view.annotation?.coordinate)!, animated: true)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
} else {
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "CustomAnnotationView")
annotationView.image = UIImage(named: "myImage")
annotationView.canShowCallout = false
return annotationView
}
}
And finally here is my CustomPin class :
var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
init(_ title: String, _ subtitle: String, _ coordinate: CLLocationCoordinate2D) {
self.title = title
self.subtitle = subtitle
self.coordinate = coordinate
}
That's how I solve this problem,
1) Create a UIView for user show where he wants to add an annotation.
2) Add a pan gesture recognizer in it.
func addPanGesture(view: UIView) {
let pan = UIPanGestureRecognizer(target: self, action: #selector (self.handlePan(sender:)))
view.addGestureRecognizer(pan)
}
3) In my selector func, I call pinDropped() func
#objc func handlePan(sender: UIPanGestureRecognizer) {
let view = sender.view!
let translation = sender.translation(in: self.mapView)
switch sender.state {
case .began, .changed:
pinImage.center = CGPoint(x: dropPinImage.center.x + translation.x, y: dropPinImage.center.y + translation.y)
sender.setTranslation(CGPoint.zero, in: view)
break
default:
pinDropped()
break
}
}
4) I write what will be happening in my pinDropped func
func pinDropped() {
DispatchQueue.main.async {
let pin = CustomPin(self.lastOrigin, "pin")
self.mapView.addAnnotation(pin)
}
self.saveButton.alpha = 1
pinImage.alpha = 0
}

Save map annotation using UserDefaults

I have been searching relentlessly for this solution and left with no choice except to rely on the expertise here on Stackoverflow. I am working on saving a map annotation if the app closes and I have been using the UserDefault to save the annotation.
This is the Objective C code that I found and I tried converting it into Swift and I think there is an error with it. I am not too sure. I place this code at viewDidLoad()
This is the save annotation
var pinnedAnnotation: CLLocationCoordinate2D = (parkedCarAnnotation?.coordinate)!
var coordinateData: NSData = NSData(bytesNoCopy: pinnedAnnotation, length: sizeof(pinnedAnnotation), freeWhenDone: false)
UserDefaults.standard.set(coordinateData, forKey: pinnedAnnotation)
UserDefaults.standard.synchronize()
And I needed a load annotation when the app open.
I dont know if viewDidLoad is the right place to put. Previously I put it in a mapView function of updatingLocation
Edited: Added code for further clarification of what I have done that needed to be corrected
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self as MKMapViewDelegate
checkLocationAuthorizationStatus()
tabBar.delegate = self
if UserDefaults.standard.object(forKey: "pinnedAnnotation") != nil {
let annotation = MKPointAnnotation()
self.mapView.addAnnotation(annotation)
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let pinnedAnnotation: CLLocationCoordinate2D = (parkedCarAnnotation?.coordinate)!
let locationData = ["latitude": parkedCarAnnotation?.coordinate.latitude, "longitude": parkedCarAnnotation?.coordinate.longitude]
UserDefaults.standard.set(locationData, forKey: "pinnedAnnotation")
UserDefaults.standard.synchronize()
print("Saving data ", UserDefaults.standard.set(locationData, forKey: "pinnedAnnotation"))
}
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if(item.tag == 0){
if mapView.annotations.count == 1{
mapView.addAnnotation(parkedCarAnnotation!)
} else {
mapView.removeAnnotation(mapView.annotations as! MKAnnotation)
}
}
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if let annotation = annotation as? ParkingSpot{
let identifier = "pin"
var view: MKPinAnnotationView
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.animatesDrop = true
view.pinTintColor = UIColor.orange
view.calloutOffset = CGPoint(x: -8, y: -3)
view.rightCalloutAccessoryView = UIButton.init(type:.detailDisclosure) as UIView
return view
} else {
return nil
}
}
extension ViewController: CLLocationManagerDelegate{
func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
centerMapOnLocation(location: CLLocation(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude))
let locationServiceCoordinate = LocationService.instance.locationManager.location!.coordinate
parkedCarAnnotation = ParkingSpot(title: "My Parking Spot", locationName: "Find your way back to your car location", coordinate: CLLocationCoordinate2D(latitude: locationServiceCoordinate.latitude, longitude: locationServiceCoordinate.longitude))
}
}
I'm not entirely sure what you're asking...
For starters when you're saving data to UserDefaults the key needs to be a string, I also believe you'll need to save you data in UserDefaults as a Dictionary
let locationData = ["lat": parkedCarAnnotation?.coordinate?.latitude, "long": parkedCarAnnotation?.coordinate.longitude]
UserDefaults.standard.set(locationData, forKey: "pinned_annotation")
And then to retrieve the data you would call
if let annotationData = UserDefaults.standard.object(forKey: "pinned_annotation") as? Dictionary {
guard let lat = annotationData["lat"], let long = annotationData["long"] else { return }
let coordinate = CLLocationCoordinate2D(latitude: lat, longitude: long)
}
Now you should hopefully be able to set your annotation with the coordinate

Identify MKPointAnnotation in mapView

I have at least 100 diferents Points ... how can associate each point with the position in my 'listOfPoints' assigning a tag in the position associated in viewForAnnotation .
Here i add my Points, some events will have the same title.
var listOfPoints : Array<Events> = [] // List Of all events
//add points to map
for (index, mPoints) in enumerate(listOfPoints) {
var point: MKPointAnnotation! = MKPointAnnotation()
var location = CLLocationCoordinate2D(latitude: mPoints.latitude, longitude: mPoints.longitude)
point.coordinate = location
point.title = mPoints.name
point.subtitle = mPoints.address
self.mapView.addAnnotation(point)
}
//Draw custom pin in the map
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
print("ffff");
var identifier = "CustomAnnotation"
if annotation.isKindOfClass(MKPointAnnotation) {
var pin = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
if pin == nil {
pin = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
pin.tag = tagPosition; // here
pin.image = UIImage(named: "mapa_pin")
pin.centerOffset = CGPointMake(0, -10)
pin.canShowCallout = true
var pointTitle = pin!.annotation.title! as String
// Callout
var button = UIButton.buttonWithType(.DetailDisclosure) as UIButton
pin!.leftCalloutAccessoryView = button
var image = UIImageView(image: UIImage(named: "mapa_pin"))
pin!.rightCalloutAccessoryView = image
} else {
pin!.annotation = annotation
}
return pin
}
return nil
}
// Print the position
func mapView(mapView: MKMapView!, didSelectAnnotationView view: MKAnnotationView!) {
println(view.tag);
}
How can associated this tag with the position on my 'listOfPoints'
pin.tag = tagPosition;
or is there another way?
I think that is easy and im doing this in an app my.
I customize a class for Annotation from MKAnnotation:
import MapKit
import AddressBook
class MyAnnotation: NSObject, MKAnnotation
{
let identifier : String
let title: String
let subtitle: String
let coordinate: CLLocationCoordinate2D
let color: MKPinAnnotationColor
init(identifier: String, title: String, subtitle: String, coordinate: CLLocationCoordinate2D, color: MKPinAnnotationColor)
{
self.identifier = identifier
self.title = title
self.subtitle = subtitle
self.coordinate = coordinate
self.color = color
super.init()
}
func mapItem() -> MKMapItem
{
let addressDictionary = [String(kABPersonAddressStreetKey): subtitle]
let placemark = MKPlacemark(coordinate: coordinate, addressDictionary: addressDictionary)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = title
return mapItem
}
}
Now you can use the field identifier from class MyAnnotation in your points:
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView!
{
if let annotation = annotation as? MyAnnotation
{
let identifier = annotation.identifier
var view: MKPinAnnotationView
if let dequeuedView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier) as? MKPinAnnotationView
{
view = dequeuedView
view.annotation = annotation
}
else
{
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.animatesDrop = true
view.leftCalloutAccessoryView = UIButton.buttonWithType(UIButtonType.DetailDisclosure) as! UIView
view.rightCalloutAccessoryView = UIButton.buttonWithType(UIButtonType.ContactAdd) as! UIView
view.pinColor = annotation.color
//view.image
//MKAnnotationView 32 x 29
}
return view
}
return nil
}
If you don't understand you can red this excelente article:
http://www.raywenderlich.com/90971/introduction-mapkit-swift-tutorial
The problem is that you are using a plain vanilla built-in MKPointAnnotation. It has no tag property. You need to define your own MKAnnotation class (i.e. a class that adopts the MKAnnotation protocol). That way, it can have any properties you like.
My very easy solution on Objective-C
.h file:
#import <MapKit/MapKit.h>
#interface mapMKPointAnnotation : MKPointAnnotation
#property (nonatomic) NSInteger tag;
#end
and create object with tag:
mapMKPointAnnotation *annotation = [[mapMKPointAnnotation alloc] init];
[annotation setTag:1];
next get tag in method:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
static NSString *SFAnnotationIdentifier = #"SFAnnotationIdentifier";
mapMKPointAnnotation *mMKPAnn = (mapMKPointAnnotation *) annotation;
NSString *img = [mMKPAnn tag] == 0 ? #"map_icon" : #"map_icon_active";