Core Data Error when segueing: Failed to call designated initializer on NSManagedObject class - ios

I have a view controller that segues to another view controller when a pin on the mapView is tapped. "Pin" is an NSManagedObject that gets populated with latitude and longitude before being sent to the second view controller (where a function populates it with actual images).
I am getting the error "CoreData: error: Failed to call designated initializer on NSManagedObject class 'Pin'" after my didSelect function is completed and before my performSegue function is called.
#nonobjc public class func fetchRequest() -> NSFetchRequest<Pin> {
return NSFetchRequest<Pin>(entityName: "Pin")
}
#NSManaged public var latitude: Double
#NSManaged public var longitude: Double
#NSManaged public var images: NSSet?
This is the class:
convenience init(latitude: Double, longitude: Double, context: NSManagedObjectContext) {
if let entity = NSEntityDescription.entity(forEntityName: "Pin", in: context) {
self.init(entity: entity, insertInto: context)
self.latitude = latitude
self.longitude = longitude
} else {
fatalError("Unable to find entity name")
}
}
I have tried declaring pin as an optional and force declaring it with "!". None of these two options works. Based on what I've learned and read on many posts online I will need to initialize the pin variable using the designated initializer. This is my attempt at the top of the view controller class, but it's not working either.
var pin = Pin.init(entity: NSEntityDescription.entity(forEntityName: "Pin", in: CoreDataStack.sharedInstance().context)!, insertInto: CoreDataStack.sharedInstance().context)
Creating the pin:
// Create the annotation
let touchPoint = gestureRecognizer.location(in: mapView)
let newCoordinate = self.mapView.convert(touchPoint, toCoordinateFrom:self.mapView)
let annotation = MKPointAnnotation()
annotation.coordinate = newCoordinate
pin = Pin(latitude: annotation.coordinate.latitude, longitude: annotation.coordinate.longitude, context: CoreDataStack.sharedInstance().context)
let pinAnnotation = PinAnnotation(objectID: pin.objectID, title: nil, subtitle: nil, coordinate: annotation.coordinate)
// Add the annotation
mapView.addAnnotation(pinAnnotation)
CoreDataStack.sharedInstance().saveContext()
Selecting the pin in didSelect:
do {
let pinAnnotation = view.annotation as! PinAnnotation
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Pin")
let predicate = NSPredicate(format: "latitude == %# AND longitude == %#", argumentArray: [pinAnnotation.coordinate.latitude, pinAnnotation.coordinate.longitude])
fetchRequest.predicate = predicate
let pins = try CoreDataStack.sharedInstance().context.fetch(fetchRequest) as? [Pin]
pin = pins![0]
} catch let error as NSError {
print("failed to get pin by object id")
print(error.localizedDescription)
return
}
self.performSegue(withIdentifier: "collectionViewSegue", sender: self)
...and performing the segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "collectionViewSegue" {
let controller = segue.destination as! CollectionViewController
print("CoreDataStack context in segue= \(CoreDataStack.sharedInstance().context)")
controller.selectedPin = pin
if let images = pin.images?.allObjects as? [Images] {
controller.photos = images
}
print("PrepareForSegue pin properties are: \n latitude: \(pin.latitude) \n longitude: \(pin.longitude)")
}
}
What am I missing?

I resolved this by making my variable declaration optional in the beginning of my class:
var selectedPin: Pin?
...and initializing differently within my long press gestureRecognizer function:
#objc func handleLongPress(_ gestureRecognizer : UIGestureRecognizer) {
if gestureRecognizer.state != .began { return }
print("Tap gesture recognized")
// Create the annotation
let touchPoint = gestureRecognizer.location(in: mapView)
let newCoordinate = self.mapView.convert(touchPoint, toCoordinateFrom:self.mapView)
let annotation = MKPointAnnotation()
annotation.coordinate = newCoordinate
// Initialize NSManagedObject 'Pin' with properties
selectedPin = Pin(context: CoreDataStack.sharedInstance().context)
selectedPin?.latitude = annotation.coordinate.latitude
selectedPin?.longitude = annotation.coordinate.longitude
if let selectedPin = selectedPin {
let pinAnnotation = PinAnnotation(objectID: selectedPin.objectID, title: nil, subtitle: nil, coordinate: annotation.coordinate)
// Add the annotation
mapView.addAnnotation(pinAnnotation)
}
CoreDataStack.sharedInstance().saveContext()
print("This is what the ole save looks like: \(CoreDataStack.sharedInstance().context)")
}

Related

How can I show the annotation point information on the map?

When I click Annotation on the map, I want the information about the clicked location to appear in the back view at the bottom.I used Mapkit to create the map
Annotation Array
let annotationLocations = [
["title":"X vet Clinic","latitude":39.895177 , "longitude":32.838194],
["title":"Y Vet Clinic","latitude": 39.894749, "longitude":32.841074],
["title":"Z Vet Clinic","latitude": 39.893615, "longitude":32.841476]
]
With this function, I can show the locations specified in the latitude longitudes above on the map
func createAnnotations(locations: [[String: Any]])
{
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)
myMap.addAnnotation(annotations)
}
}
You can subclass your annotation and put the required data in there;
class VetClinicAnnotation: MKPointAnnotation {
let name: String
let address: String
let image: UIImage
init(with name: String, address: String, image: UIImage) {
self.name = name
self.address = address
self.image = image
}
}
Then you can get the annotation info from your map view delegate;
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if let annotation = view.annotation as? VetClinicAnnotation {
// Use the annotation data
}
}
First set the delegate
myMap.delegate = self
Then implement
func mapView(_ mapView: MKMapView,didSelect view: MKAnnotationView) {
let ann = view.annotation as! MKPointAnnotation
// use ann
}

pass array of map coordinates to a draw a route on map in swift

hi I have an array of coordinates data like this, which is retrieved from an API call, and I use a for loop to append the data into an array like this:
extension TripListViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: Constants.segueTripListToTripMap, sender: indexPath)
tableView.deselectRow(at: indexPath, animated: true)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == Constants.segueTripListToTripMap {
if let destinationVC = segue.destination as? TripMapViewController,
let indexPath = sender as? IndexPath {
for i in 0...(tripList[indexPath.section][indexPath.row].coordinates.count-1) {
self.coodinates.append(tripList[indexPath.section][indexPath.row].coordinates[i])
}
destinationVC.coodinatePoints = coodinates
}
} else {
if let destinationVC = segue.destination as? PolicyOverviewViewController,
let indexPath = sender as? IndexPath {
// do something
}
}
}
}
it has an error... I don't know how to pass this array to the variable I declared in another screen, the data type seem doesn't match.
The original data type look like this:
struct Trips: Codable {
let id: String
let userId: String
let coordinates: [Coordinates]
}
struct Coordinates: Codable {
let lat: Double?
let lng: Double?
let time: String?
}
[
["time": "timestampes"
"lat": 40.213
"lon": 5.203],
["time": "timestampes"
"lat": 40.213
"lon": 5.203],
["time": "timestampes"
"lat": 40.213
"lon": 5.203],
["time": "timestampes"
"lat": 40.213
"lon": 5.203]
]
how can I pass this data into my draw route function's point variable. it's currently hardcoded with some dummy data.
func drawRoutes() {
var points = [CLLocationCoordinate2DMake(51.079980, 4.349850),
CLLocationCoordinate2DMake(51.079060, 4.350830),
CLLocationCoordinate2DMake(51.078210, 4.350490),
CLLocationCoordinate2DMake(51.077750, 4.350890),
CLLocationCoordinate2DMake(51.076760, 4.354600),
CLLocationCoordinate2DMake(51.075130, 4.351000),
CLLocationCoordinate2DMake(51.073800, 4.350690),
CLLocationCoordinate2DMake(52.071850, 4.352880),
CLLocationCoordinate2DMake(52.069320, 4.355940),
CLLocationCoordinate2DMake(52.069120, 4.356130),
CLLocationCoordinate2DMake(52.069120, 4.356130),
CLLocationCoordinate2DMake(52.069120, 4.356130),
CLLocationCoordinate2DMake(52.068570, 4.356950),
CLLocationCoordinate2DMake(52.067840, 4.358440),
CLLocationCoordinate2DMake(52.066730, 4.357490),
CLLocationCoordinate2DMake(52.066590, 4.358680),
CLLocationCoordinate2DMake(52.066580, 4.358680),
CLLocationCoordinate2DMake(52.066580, 4.358680),
CLLocationCoordinate2DMake(52.066830, 4.357490),
CLLocationCoordinate2DMake(52.067600, 4.358520),
CLLocationCoordinate2DMake(52.068650, 4.356920),
CLLocationCoordinate2DMake(52.074330, 4.350360),
CLLocationCoordinate2DMake(52.075520, 4.351880),
CLLocationCoordinate2DMake(52.076950, 4.355350),
CLLocationCoordinate2DMake(52.078000, 4.350690),
CLLocationCoordinate2DMake(52.078010, 4.350710),
CLLocationCoordinate2DMake(52.079520, 4.351560),
CLLocationCoordinate2DMake(52.080680, 4.350220),
CLLocationCoordinate2DMake(52.080760, 4.348890),
CLLocationCoordinate2DMake(52.079890, 4.349980),
CLLocationCoordinate2DMake(52.079890, 4.350000)]
let polygon = MKPolyline(coordinates: &points, count: points.count)
self.mapView.addOverlay(polygon)
self.mapView.setVisibleMapRect(polygon.boundingMapRect, animated: true)
var startPoint = points[0]
for i in 1...(points.count-1) {
guard let request = createRequest(c1:startPoint, c2:points[i]) else { return }
let directions = MKDirections(request: request)
directions.calculate { [unowned self] (response, error) in
guard let response = response else { return }
let routes = response.routes
let bestDest = routes[0]
startPoint = points[i]
}
}
}
thanks a lot !
You can use map to convert your coordinates
let points: [CLLocationCoordinate2D] = coordinates.compactMap {
guard let latitude = $0.lat, let longitude = $0.lng else { return nil }
return CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
}
This will convert all Coordinates objects that has both a latitude and a longitude given.
Are you sure the properties can be null in Coordinates, if not the above can be simplified to
let points: [CLLocationCoordinate2D] = coordinates.map {
CLLocationCoordinate2D(latitude: $0.lat, longitude: $0.lng)
}
based on Joakim's answer, I figured out the complete solution for this:
So what do you do to pass some coordinates data from screen A (say you have a tableView) to screen B (you have the map).
So in screen A (viewControllerA):
You define the coordinates variable (according to your data model of course, should be an array usually)
var coordinates = [Coordinates]()
Then in your UITableViewDelegate extension, you append your coordinates into your coordinates array. Then use prepare segue to pass it to your destination viewController.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == Constants.segueTripListToTripMap {
if let destinationVC = segue.destination as? TripMapViewController,
let indexPath = sender as? IndexPath {
for i in 0...(tripList[indexPath.section][indexPath.row].coordinates.count-1) {
self.coordinates.append(tripList[indexPath.section][indexPath.row].coordinates[i])
}
destinationVC.coordinatePoints = coordinates
coordinates = []
}
} else {
if let destinationVC = segue.destination as? PolicyOverviewViewController,
let indexPath = sender as? IndexPath {
// do something
}
}
}
In your screen B (where you map is). You define the variable to catch the data pass from screen A, which is coordinatePoints
var coordinatePoints: [Coordinates]?
then in your draw route function (or whatever name you call it). You convert this coordinatePoints into CLLocationCoordinate2D type like below:
func drawRoutes() {
var points:[CLLocationCoordinate2D] = coordinatePoints?.compactMap {
guard let latitude = $0.lat, let longitude = $0.lng else { return nil }
return CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
} ?? [CLLocationCoordinate2DMake(52.3676, 4.9041)]
let polygon = MKPolyline(coordinates: &points, count: points.count)
self.mapView.addOverlay(polygon)
self.mapView.setVisibleMapRect(polygon.boundingMapRect, animated: true)
var startPoint = points[0]
for i in 1...(points.count-1) {
guard let request = createRequest(c1:startPoint, c2:points[i]) else { return }
let directions = MKDirections(request: request)
directions.calculate { [unowned self] (response, error) in
guard let response = response else { return }
let routes = response.routes
let bestDest = routes[0]
startPoint = points[i]
}
}
}
there you go, when you click the table cell in screen A, it should pass the right coordinates into screen B and plot it on the map.

Find selected MapKit Annotation's postID

I'm trying to set the selected MapKit's Annotation ID (mapEventID in my case) on didSelect and to use when Swift is preparing the segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let postViewVC = segue.destination as! PostView
postViewVC.eventID = mapEventID
}
mapEventID is initialized in the GeoFire observer:
func retrieveAllEvents () {
let postDB = Database.database().reference().child("DBName")
postDB.observe(.childAdded) { (snapshot) in
let snapshotValue = snapshot.value as! Dictionary <String,AnyObject>
let postTitle = snapshotValue ["EventTitle"]!
let postLocation = snapshotValue ["VisibleLocation"]!
let eventID = snapshotValue ["EventID"]
let postTime = snapshotValue ["EventDateTimeStart"]!
let date = self.convertTimestamp(serverTimestamp: postTime as! Double)
let mapEventDetails : String = "\(date) - \(postLocation)"
self.geoFire.getLocationForKey(locationID! as! String) { (location, error) in
if (error != nil) {
print("An error occurred getting the location for eventID:", eventID as Any)
} else if (location != nil) {
let postLat = location?.coordinate.latitude
let postLong = location?.coordinate.longitude
let coordinate = CLLocationCoordinate2D(latitude: postLat!, longitude: postLong!)
let annotation = EventAnnotation (coordinate: coordinate, withKey: eventID as! String, title: mapEventDetails, subtitle: mapEventDetails)
self.mapEventID = annotation.eventKey
self.eventMap.addAnnotation(annotation)
} else {
print("GeoFire does not contain a location for:", eventID as Any)
}
}
}
}
The problem is that it always uses the most recently added post's ID as the mapEventID and not the one the user actually tapped on. This makes sense because when the IDs are retrieved from Firebase through the observer it 'observes' for all the keys and the most recent one becomes the ID.
How do I obtain the specific mapEventID when the user taps on the annotation? At the moment I have:
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
mapEventID = //How do I change the EventID here?
}
I was hoping I could add the ID to the annotation's Description' property in the Geofire observer but Swift does not provide any syntax for that in:
let annotation = EventAnnotation (coordinate: coordinate, withKey: eventID as! String, title: postTitle, subtitle: eventDetails)
Try the following code:
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if let annotation = view.annotation as? EventAnnotation {
mapEventID = annotation.eventKey
}
}

Strange behaviour in showing annotations images on map using data coming from Firebase. SWIFT 4.1

The strange behaviour is that when I add a new annotation, either tapped or user location, it gets displayed with the right chosen icon. When MapVC load for the first time, the posts retrieved from Firebase have all the same icon, ( the icon name of the latest one posted. If, after posting a new one, I exit mapViewVc to the menuVC and re enter mapViewVC than every icon is displaying the same icon again, now being my previously posted one.
a Few times it happened the the icons were two different icons, randomly chosen.
I don't understand why the coordinates are taken right but the image is not.
The app flow is:
I have a mapView vc where I can either double tap on screen and get coordinate or code user location coordinate via a button and then get to an chooseIconVc where I have all available icons to choose for the annotation. Once I select one, the icon name get passed back in in mapViewVC in unwindHere() that stores icon name into a variable and coordinates into another. In postAlertNotification those variables get posted to Firebase.
In displayAlerts() the data from Firebase gets stored into variables to initialise an annotation and gets added to mapView.
chosen icon:
#IBAction func unwindHere(sender:UIStoryboardSegue) { // data coming back
if let sourceViewController = sender.source as? IconsViewController {
alertNotificationType = sourceViewController.dataPassed
if tapCounter > 0 {
alertNotificationLatitude = String(describing: alertCoordinates.latitude)
alertNotificationLongitude = String(describing: alertCoordinates.longitude)
postAlertNotification() // post new notification to Firebase
} else {
alertCoordinates = self.trackingCoordinates
alertNotificationLatitude = String(describing: self.trackingCoordinates!.latitude)
alertNotificationLongitude = String(describing: self.trackingCoordinates!.longitude)
postAlertNotification() // post new notification to Firebase
}
}
}
than post:
func postAlertNotification() {
// to set next notification id as the position it will have in array ( because first position is 0 ) we use the array.count as value
let latitude = alertNotificationLatitude
let longitude = alertNotificationLongitude
let alertType = alertNotificationType
let post: [String:String] = [//"Date" : date as! String,
//"Time" : time as! String,
"Latitude" : latitude as! String,
"Longitude" : longitude as! String,
"Description" : alertType as! String]
var ref: DatabaseReference!
ref = Database.database().reference()
ref.child("Community").child("Alert Notifications").childByAutoId().setValue(post)
}
retrieve and display:
func displayAlerts() {
ref = Database.database().reference()
databaseHandle = ref?.child("Community").child("Alert Notifications").observe(.childAdded, with: { (snapshot) in
// defer { self.dummyFunctionToFoolFirebaseObservers() }
guard let data = snapshot.value as? [String:String] else { return }
guard let firebaseKey = snapshot.key as? String else { return }
// let date = data!["Date"]
// let time = data!["Time"]
let dataLatitude = data["Latitude"]!
let dataLongitude = data["Longitude"]!
self.alertIconToDisplay = data["Description"]!
let doubledLatitude = Double(dataLatitude)
let doubledLongitude = Double(dataLongitude)
let recombinedCoordinate = CLLocationCoordinate2D(latitude: doubledLatitude!, longitude: doubledLongitude!)
print("Firebase post retrieved !")
print("Longitude Actual DataKey is \(String(describing: firebaseKey))")
print("fir long \((snapshot.value!, snapshot.key))")
self.userAlertAnnotation = UserAlert(type: self.alertIconToDisplay!, coordinate: recombinedCoordinate, firebaseKey: firebaseKey)
self.mapView.addAnnotation(self.userAlertAnnotation)
})
}
and
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = MKAnnotationView(annotation: userAlertAnnotation, reuseIdentifier: "") // CHANGE FOR NEW ANNOTATION : FULL DATA
//added if statement for displaying user location blue dot
if annotation is MKUserLocation{
return nil
} else {
annotationView.image = UIImage(named: alertIconToDisplay!) // choose the image to load
let transform = CGAffineTransform(scaleX: 0.27, y: 0.27)
annotationView.transform = transform
return annotationView
}
}
the variables declarations :
var alertIconToDisplay: String?
var userAlertAnnotation: UserAlert!
var alertNotificationType: String?
var alertNotificationLatitude: String?
var alertNotificationLongitude: String?
UPDATE:
annotation cLass:
import MapKit
class UserAlert: NSObject , MKAnnotation {
var type: String?
var firebaseKey: String?
var coordinate = CLLocationCoordinate2D()
var image: UIImage?
override init() {
}
init(type:String, coordinate:CLLocationCoordinate2D, firebaseKey: String) {
self.type = type
self.firebaseKey = firebaseKey
self.coordinate = coordinate
}
}
After understanding where the problem I was explained how to changed the displayAlert() into
func displayAlerts() { // rajish version
ref = Database.database().reference()
databaseHandle = ref?.child("Community").child("Alert Notifications").observe(.childAdded, with: { (snapshot) in
// defer { self.dummyFunctionToFoolFirebaseObservers() }
guard let data = snapshot.value as? [String:String] else { return }
guard let firebaseKey = snapshot.key as? String else { return }
// let date = data!["Date"]
// let time = data!["Time"]
let dataLatitude = data["Latitude"]!
let dataLongitude = data["Longitude"]!
let type = data["Description"]!
let id = Int(data["Id"]!)
let doubledLatitude = Double(dataLatitude)
let doubledLongitude = Double(dataLongitude)
let recombinedCoordinate = CLLocationCoordinate2D(latitude: doubledLatitude!, longitude: doubledLongitude!)
print("Firebase post retrieved !")
print("Longitude Actual DataKey is \(String(describing: firebaseKey))")
print("fir long \((snapshot.value!, snapshot.key))")
var userAlertAnnotation = UserAlert(type: type, coordinate: recombinedCoordinate, firebaseKey: firebaseKey, title: type,id: id!)
self.userAlertNotificationArray.append(userAlertAnnotation) // array of notifications coming from Firebase
print("user alert array after append from Firebase is : \(self.userAlertNotificationArray)")
self.alertNotificationArray.append(recombinedCoordinate) // array for checkig alerts on route
self.mapView.addAnnotation(userAlertAnnotation)
})
}
and the mapView to:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { // rajish version
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "")
if annotation is MKUserLocation{
return nil
} else {
print(annotation.coordinate)
annotationView.image = UIImage(named:(annotationView.annotation?.title)! ?? "")
// annotationView.canShowCallout = true
let transform = CGAffineTransform(scaleX: 0.27, y: 0.27)
annotationView.transform = transform
return annotationView
}
}
that solved it.

Sending Images to Several Instances of a ViewController in Swift

I download several "events" from CloudKit in my program. Each one contains a picture, title, and location. The second two are used right away and there is no problem. The image is only seen if the user clicks on the info button on the annotation that shows the picture using a separate view controller. I am having trouble getting the images to be passed to the ViewController before it is used while also keeping each event's picture separate.
Here is where each record is queried from CloudKit and put into a for loop that send then to the method below:
func loadEvent(_ completion: #escaping (_ error:NSError?, _ records:[CKRecord]?) -> Void)
{
//...record is downloaded from cloudkit
for record in records
{
if let asset = record["Picture"] as? CKAsset,
let data = NSData(contentsOf: asset.fileURL),
let image1 = UIImage(data: data as Data)
{
self.drawEvents(record["LocationF"] as! CLLocation, title1: record["StringF"] as! String, pic1: image1)
}
}
}
Here is where the variables are assigned and used to create the point annotation (it is a custom one that includes an image):
func drawEvents(_ loc: CLLocation, title1: String, pic1: UIImage)
{
mapView.delegate = self
let center = CLLocationCoordinate2D(latitude: loc.coordinate.latitude, longitude: loc.coordinate.longitude)
let lat: CLLocationDegrees = center.latitude
let long: CLLocationDegrees = center.longitude
self.pointAnnotation1 = CustomPointAnnotation()
self.pointAnnotation1.imageName = pic1
self.pointAnnotation1.title = title1
self.pointAnnotation1.subtitle = "Event"
self.pointAnnotation1.coordinate = CLLocationCoordinate2D(latitude: lat, longitude: long)
self.pinAnnotationView = MKPinAnnotationView(annotation: self.pointAnnotation1, reuseIdentifier: nil)
self.mapView.addAnnotation(self.pinAnnotationView.annotation!)
}
Here is the function that makes EventPage appear when the info button on an MKMapAnnotation is clicked:
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let eventPageViewController:EventPageViewController = storyboard?.instantiateViewController(withIdentifier: "EventPage") as! EventPageViewController
self.present(eventPageViewController, animated: true, completion: nil)
}
Here is the ViewController for EventPage:
class EventPageViewController: UIViewController {
#IBOutlet weak var eventPic: UIImageView!
var photo: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
let firstViewController:FirstViewController = storyboard?.instantiateViewController(withIdentifier: "Home") as! FirstViewController
photo = firstViewController.pointAnnotation1.imageName
//photo is nil
eventPic.image = photo
}
}
Intead of storing your annotations in a single instance property (which means that you only have access to the last one), store them in an array, although you don't need to store them at all in order to respond to taps.
var annotations = [CustomPointAnnotation]()
func drawEvents(_ loc: CLLocation, title1: String, pic1: UIImage)
{
mapView.delegate = self
let center = CLLocationCoordinate2D(latitude: loc.coordinate.latitude, longitude: loc.coordinate.longitude)
let lat: CLLocationDegrees = center.latitude
let long: CLLocationDegrees = center.longitude
var newAnnotation = CustomPointAnnotation()
newAnnotation = CustomPointAnnotation()
newAnnotation.imageName = pic1
newAnnotation.title = title1
newAnnotation.subtitle = "Event"
newAnnotation.coordinate = CLLocationCoordinate2D(latitude: lat, longitude: long)
self.mapView.addAnnotation(newAnnotation)
self.annotations.append(newAnnotation)
}
The annotation view that is tapped is passed to your delegate method, so you know which item it is. The annotation view's annotation property is your CustomPointAnnotation instance.
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let eventPageViewController:EventPageViewController = storyboard?.instantiateViewController(withIdentifier: "EventPage") as! EventPageViewController
if let annotation = view.annotation as CustomPointAnnotation {
eventPageViewController.photo = annotation.imageName
}
self.present(eventPageViewController, animated: true, completion: nil)
}

Resources