I'm building a application that allows people to post annotations on a map and you can click on the detail closure of the annotation and it loads a ViewController with the image and the subtitle information but when I try to load the image and subtitle in the viewDidLoad method of the detailVC it says it's nil
Here's my Annotation code:
extension ViewController: MKMapViewDelegate {
private struct Constants{
static let identifier = "pin"
static let LeftCalloutFrame = CGRect(x:0, y:0, width: 59, height: 59)
static let ShowPinSegueIdentifier = "showDetail"
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if let annotation = annotation as? PingAnnotation {
var view = mapView.dequeueReusableAnnotationViewWithIdentifier(Constants.identifier) as? MKPinAnnotationView
if view == nil {
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: Constants.identifier)
view?.canShowCallout = true
} else {
view!.annotation = annotation
}
view!.calloutOffset = CGPoint(x: -5, y: 5)
view!.leftCalloutAccessoryView = UIImageView(frame: Constants.LeftCalloutFrame)
view!.rightCalloutAccessoryView = UIButton(type: .DetailDisclosure) as UIView
return view
}
return nil
}
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
if let thumbnailImageView = view.leftCalloutAccessoryView as? UIImageView {
if let pingAnnotation = view.annotation as? PingAnnotation{
thumbnailImageView.image = pingAnnotation.image
}
}
}
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
performSegueWithIdentifier(Constants.ShowPinSegueIdentifier, sender: view)
}
}
Annotation code:
class PingAnnotation: NSObject, MKAnnotation {
var coordinate : CLLocationCoordinate2D
var title: String?
var subtitle: String?
var image : UIImage?
init(coordinate: CLLocationCoordinate2D, title: String?, subtitle: String?, image: UIImage?) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
self.image = image
}
}
Detail VC Code:
import UIKit
class DetailPingVC: UIViewController {
var ping : PingAnnotation!
#IBOutlet var pingImage: UIImageView!
#IBOutlet var pingDesc: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
pingImage.image = ping.image //shows as nil when ran
pingDesc.text = ping.subtitle //shows as nil when ran
}
#IBAction func pingBtnPressed(sender: AnyObject) {
}
#IBAction func backBtnPressed(sender: AnyObject) {
dismissViewControllerAnimated(true, completion: nil)
}
}
EDIT:
I tried using prepareForSegue but the values still come out nil
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destViewController: DetailPingVC = segue.destinationViewController as! DetailPingVC
destViewController.pingImage.image = ping.image
destViewController.pingDesc.text = ping.subtitle
}
Got it! Basically what I did was add a variable of type string and a variable of type UIImage to the DetailVC and then my prepareForSegue in my viewController looks as so:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destViewController: DetailPingVC = segue.destinationViewController as! DetailPingVC
destViewController.descText = postTextField.text!
destViewController.detailImage = pickImage.image!
}
then in the viewDidLoad of the Detail VC you set the Outlets to the variables you set up earlier.
Related
I am struggling with my iOS code. The overall goal of the project is to show the pins on the map, I've tried multiple ways to make the pins appear on the map such as the below, I am not sure what I am doing wrong. The code runs fine, the pins are not appearing. I've made changes to my functions but seem to get the same error. Any insight on what I may be missing?
"import UIKit
import MapKit
class MapViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
#IBOutlet weak var logout: UIBarButtonItem!
#IBOutlet weak var refresh: UIBarButtonItem!
#IBOutlet weak var pinDrop: UIBarButtonItem!
var studentLocation = [StudentInformation]()
var annotations = [MKPointAnnotation]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear (_ animated: Bool) {
super.viewDidAppear(true)
getStudentPins()
}
#IBAction func refresh(_ sender: UIBarButtonItem) {
getStudentPins ()
}
#IBAction func logout(_ sender: UIBarButtonItem) {
self.activityIndicator.startAnimating()
UdacityClient.logout {
DispatchQueue.main.async {
self.dismiss(animated: true, completion: nil)
self.activityIndicator.stopAnimating()
}
}
}
#IBAction func addLocation(_ sender: Any) {
performSegue(withIdentifier: "addLocation", sender: nil)
}
func getStudentPins () {
self.activityIndicator.startAnimating()
UdacityClient.getStudentLocations() { locations, error in
self.mapView.removeAnnotations(self.annotations)
self.annotations.removeAll()
self.studentLocation = locations ?? []
for dictionary in locations ?? [] {
let latitude = CLLocationDegrees(dictionary.latitude ?? 0.0)
let longitude = CLLocationDegrees(dictionary.longitude ?? 0.0)
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let firstName = dictionary.firstName
let lastName = dictionary.lastName
let mediaURL = dictionary.mediaURL
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = "\(firstName) \(lastName)"
annotation.subtitle = mediaURL
self.annotations.append(annotation)
}
DispatchQueue.main.async {
self.mapView.addAnnotations(self.annotations)
self.activityIndicator.stopAnimating()
}
}
}
private func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotation? {
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.pinTintColor = .green
pinView!.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
}
else {
pinView!.annotation = annotation
}
return pinView?.annotation
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
if let toOpen = view.annotation?.subtitle {
openLink(toOpen ?? "")
}
}
}
}"
They're called annotations in the MapKit and you should instantiate them like so:
let annotation = MKPointAnnotation()
then in the viewDidLoad() method just set the coordinates and add them to the map like:
annotation.coordinate = CLLocationCoordinate2D(latitude: 11.12, longitude: 12.11)
mapView.addAnnotation(annotation)
The numbers are your coordinates. Try by adding sample coordinates on the map first and then add all coordinates which you are getting from the client.
you can also add more information using:
annotation.title = "Your text here"
//You can also add a subtitle that displays under the annotation such as
annotation.subtitle = "One day I'll go here..."
class MapViewController: UIViewController, MKMapViewDelegate, HomeModelProtocol {
var feedItems: NSArray = NSArray()
var selectedLocation : LocationModel = LocationModel()
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let initialLocation = CLLocation(latitude: 45.444958, longitude: 12.328463)
centerMapLocation(location: initialLocation)
mapView.delegate = self
let homeModel = HomeModel()
homeModel.delegate = self
homeModel.downloadItems()
}
func itemsDownloaded(items: NSArray) {
feedItems = items
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
let regionRadus: CLLocationDistance = 1000
func centerMapLocation(location: CLLocation){
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate, regionRadus, regionRadus)
mapView.setRegion(coordinateRegion, animated: true)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
//checkLocationAuthorizationStatus()
displayLocations()
}
func displayLocations(){
let i = feedItems.count
var x = 0
while x<i{
let item: LocationModel = feedItems[x] as! LocationModel
var poiCoodinates = CLLocationCoordinate2D()
poiCoodinates.latitude = CDouble(item.latitude!)!
poiCoodinates.longitude = CDouble(item.longitude!)!
let pin: MKPointAnnotation = MKPointAnnotation()
pin.coordinate = poiCoodinates
self.mapView.addAnnotation(pin)
pin.title = item.name
pin.subtitle = item.address
x = x+1
}
//return loc
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let view = MKMarkerAnnotationView(annotation: selectedLocation as? MKAnnotation, reuseIdentifier: "pin")
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.leftCalloutAccessoryView = UIButton(type: .detailDisclosure)
return view
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
print(control.tag)
selectedLocation = feedItems[0] as! LocationModel
performSegue(withIdentifier: "InformationSegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get reference to the destination view controller
let detailVC = segue.destination as! InformationViewController
// Set the property to the selected location so when the view for
// detail view controller loads, it can access that property to get the feeditem obj
detailVC.selectedLocation = selectedLocation
}
}
This is my code.
I want to display the Location in the next Viewcontroller.
I need to get the index at feeditems[].
How can i get the index in:
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl)
So how do i get the index, which Button is tapped. There are many objects that are placed in the map.
Thank you for help and sorry for my bad english, hope you guys understand me.
1.Define Sublass MKPointAnnotation.
class MyPointAnnotation: MKPointAnnotation {
var feedItem: LocationModel
}
2.Set MyPointAnnotation.feedItem to feedItem.
let item: LocationModel = feedItems[x] as! LocationModel
var poiCoodinates = CLLocationCoordinate2D()
poiCoodinates.latitude = CDouble(item.latitude!)!
poiCoodinates.longitude = CDouble(item.longitude!)!
let pin: MyPointAnnotation = MyPointAnnotation()
pin.coordinate = poiCoodinates
pin.feedItem = item // Important!
self.mapView.addAnnotation(pin)
3.Get feedItem in calloutAccessoryControlTapped delegate method.
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if let pin = view.annotation as? MyPointAnnotation {
print(pin.feedItem)
}
}
Sublass MKAnnotation add index property / object from feedItems array to the class and
see custom class MyAnnotation implemented there in swift customPinAnnotationButton
this idea but now i have only objective -c version
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
NSLog(#"wqwqwqwqwqw . . .");
MyAnnotation*ann = view.annotation;
NSLog(#"nammemmeme : %#",ann.weatherItem);
[self performSegueWithIdentifier:#"showDetails" sender:ann.weatherItem];
}
1.Define subclasses for Annotation
class PointAnnotation: MKPointAnnotation {
var indexAnnotation = 0
}
2.Mapview Delegate
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.isMember(of: MKUserLocation.self) {
return nil
}
let identifier = "myAnnotation"
var annotationView: MKAnnotationView?
annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView?.image = UIImage(named:"Golf Courses.png")
annotationView?.canShowCallout = true
let callButton = UIButton(type: .detailDisclosure)
annotationView?.rightCalloutAccessoryView = callButton
annotationView?.sizeToFit()
} else {
annotationView!.annotation = annotation
}
}
3.Callaccessory button taped go to next view contoller
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
let obj = kStoryboardShops.instantiateViewController(withIdentifier: "ShopDetailsViewController") as! ShopDetailsViewController
if let annotation = view.annotation as? PointAnnotation {
obj.dicDetails = arrayOfItems[annotation.indexAnnotation]
}
let nav = UINavigationController(rootViewController: obj)
self.present(nav, animated: true, completion: nil)
}
}
I'm struggling with passing data from a map pin to a new vc.
Scenario
Map pin data (name, subtitle & coordinates) are currently being read from a JSON file. Map pins are dropped, when map pin has been clicked a new view will open and display the corresponding title and subtitle and some other data in labels in a new vc.
I've been following this tutorial for extracting data from a JSON file.
Issue: I'm struggling to find a way to pass the data from the map pin to the labels.
I've done a previous project where I pass data from one textfield to a label.
Thank you very much!
EDIT:
I realised I didn't give any code.
newFeatureVC
// Callout accessory
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "POIAnnotations"
if annotation is POIAnnotations {
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView!.canShowCallout = true
let button = UIButton(type: .detailDisclosure)
annotationView!.rightCalloutAccessoryView = button
}
else {
annotationView!.annotation = annotation
}
return annotationView
}
return nil
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
performSegue(withIdentifier: "showAnnotationInfo", sender: self)
}
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showAnnotationInfo" {
let destViewController = segue.destination as! ShopDetailViewController
}
}
POIAnnotations
This class is where variables stored for the name, subtitle and coordinates.
class POIAnnotations: NSObject, MKAnnotation {
var title: String?
var subtitle: String?
var coordinate: CLLocationCoordinate2D
init(title:String, subtitle:String, coordinate:CLLocationCoordinate2D) {
self.title = title
self.subtitle = subtitle
self.coordinate = coordinate
super.init()
}
class func fromJSON(_ json: [JSONValue]) -> POIAnnotations? {
// 1
var title: String
if let titleOrNil = json[1].string {
title = titleOrNil
} else {
title = ""
}
var subtitle: String
if let subtitleOrNil = json[2].string {
subtitle = subtitleOrNil
} else {
subtitle = ""
}
// 2
let latitude = (json[3].string! as NSString).doubleValue
let longitude = (json[4].string! as NSString).doubleValue
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
// 3
return POIAnnotations(title: title, subtitle: subtitle, coordinate: coordinate)
}
}
SecondVC
class ShopDetailViewController: UIViewController {
#IBOutlet weak var shopName: UILabel!
#IBOutlet weak var shopRating: UILabel!
#IBOutlet weak var cancelButton: UIBarButtonItem!
var shopNameData = "name"
var shopRatingData = "rating"
What I've already tried doing is the same way that I got the data from the JSON file in the POIAnnotations class.
I tried calling the fromJSON function then tried storing it in variable to display on the label. But I couldn't get my head around how to do it.
Use MKMapViewDelegate method to detect which pin is tapped and then display its data in view controller.
func mapView(MKMapView, didSelect: MKAnnotationView)
I have created the following:
let artworkPin = Artwork(title:"Wind Wand",locationName:"Majestic",discipline:"Statue",
coordinate:windwandcoord)
where Artwork refers to a class located in Artwork.swift, I am trying to assign a label to obtain the title value (Located in a annotation on UI View 1 ) through a Segue to go to a Label ( Located in UI View 2) by doing the following:
#IBOutlet weak var art_title: UILabel!
var viaSegue = "artwork title should be here"
override func viewDidLoad() {
super.viewDidLoad()
art_title.text = viaSegue
but I don't know how to reference it correctly for via Segue to take the value of "title".
ENTIRE FILE:
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate,CLLocationManagerDelegate {
#IBOutlet weak var MapView: MKMapView!
let manager = CLLocationManager()
var artworkPin = Artwork!
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//let location = locations[0]
//let span:MKCoordinateSpan = MKCoordinateSpanMake(0.02, 0.02)
//let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
}
override func viewDidLoad() {
super.viewDidLoad()
// tracking user's location
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
// Setting up Map
let distanceSpan:CLLocationDegrees = 2000
MapView.setRegion(MKCoordinateRegionMakeWithDistance(CLLocationCoordinate2DMake(-39.0556253, 174.0752278), distanceSpan, distanceSpan), animated: true)
MapView.showsUserLocation = true
MapView.delegate = self
// artwork on map
let windwandcoord: CLLocationCoordinate2D = CLLocationCoordinate2DMake(-39.055961,174.072288)
artworkPin = Artwork(title:"Wind Wand",locationName:"Majestic",discipline:"Statue",
coordinate:windwandcoord)
MapView.addAnnotation(artworkPin)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
{
if annotation is MKUserLocation {return nil}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
pinView!.calloutOffset = CGPoint(x: -5, y: 5)
let calloutButton = UIButton(type: .detailDisclosure)
pinView!.rightCalloutAccessoryView = calloutButton
pinView!.sizeToFit()
}
else {
pinView!.annotation = annotation
}
return pinView
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
performSegue(withIdentifier: "no", sender:self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let ViewTwo = segue.destination as! ViewTwo
ViewTwo.artworkPin = self.artworkPin
}
}
Thanks for your help
In your vc B add:
var artworkpin: Artwork!
In your vc A:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vcB = segue.destinationViewController as! vcB
vcB.artworkpin = self.artworkpin
}
after that in vc B viewDidLoad you can get the title by art_title.text = artworkpin.title
Im working with Mapkit. In the leftcalloutaccessory, there is a small image, thats been taken from the userlocation. In the rightcalloutaccesory, there is a button, that will make a segue, to another viewcontroller, so the user can see the image in large size.
The issue is - it is random which saved image, that will be shown on the another view.
class ImageAnnotation: MKPointAnnotation {
var image: UIImage?
}
Then in the Map view controller it looks like this
var imageAnnotations: [ImageAnnotation] = []
var sendImage: UIImageView!
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
guard let annotation = annotation as? ImageAnnotation else {
return nil
}
let identifier = "MyCustomAnnotation"
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView?.canShowCallout = true
} else {
annotationView!.annotation = annotation
}
let image = UIImage(named: "advance.png")
let button = UIButton(type: .DetailDisclosure)
button.frame = CGRectMake(0, 0, 30, 30)
button.setImage(image, forState: .Normal)
annotationView?.rightCalloutAccessoryView = button
let detailImage = UIImageView(frame: CGRectMake(0, 0, 50, 50))
detailImage.image = annotation.image
sendImage.image = annotation.image
annotationView?.leftCalloutAccessoryView = detailImage
return annotationView
}
It works. Its showing the right image in every annotation. But "pickedimage", can be every saved images.
override func viewDidAppear(animated: Bool) {
super.viewWillAppear(animated)
for annotation in imageAnnotations {
mapView.addAnnotation(annotation)
}
gettingData()
}
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl)
{
if (view.annotation is MKPointAnnotation) {
print("Clicked annotation ")
if control == view.rightCalloutAccessoryView {
sendImage.image = UIImage(named: "advance.png")
}
}
}
func buttonPressed (sender: UIButton!) {
let currentImage: UIImage = sender.imageForState(.Normal)!
sendImage.image = currentImage
self.performSegueWithIdentifier("ShowLargeImage", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowLargeImage" {
let goToImageViewController = segue.destinationViewController as! ImageViewController
goToImageViewController.newImage = sendImage.image
}
}
In destination view controller:
class ImageViewController: UIViewController
{
#IBOutlet weak var finalImage: UIImageView! // assume that its your imageViewname
var newImage: UIImage! // assume that it is pass Image
override func viewDidLoad() {
super.viewDidLoad()
if let img = newImage
{
finalImage.image = img
}
}
}
In Source view controller:
//create one Common ImageView
var sendImage: UIImageView!
func buttonPressed (sender: UIButton!) {
self.performSegueWithIdentifier("ShowLargeImage", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowLargeImage" {
let goToImageViewController = segue.destinationViewController as! ImageViewController
goToImageViewController.newImage = sendImage.image // directly pass the image no need of converson
}
}
at the same time add here also
let detailImage = UIImageView(frame: CGRectMake(0, 0, 50, 50))
detailImage.image = annotation.image
sendImage.image = annotation.image
annotationView?.leftCalloutAccessoryView = detailImage
updated answer
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl)
{
if (view.annotation is MKPointAnnotation) {
print("Clicked annotation ")
if control == view.rightCalloutAccessoryView {
sendImage.image = UIImage(named: "advance.png")
}
}
}