I do not find how link my let = pin
Help me please
My first code:
#IBOutlet weak var Point1S: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
let location = CLLocationCoordinate2DMake(43.429837, 5.434837)
Point1S.setRegion(MKCoordinateRegionMakeWithDistance(location, 500, 500), animated: true)
let pin = here
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
Second code:
import MapKit
class MKPinAnnotationView : 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
}
}
I do not know how to put on my first code in the let pin = the second code
thanks
let pin = MKPinAnnotationView(title: "title", subtitle: "subtitle", coordinate: CLLocationCoordinate2DMake(27.33, 85.03))
Related
I'm struggling to get KVO updates within a callout already displayed.
My use case: I want to display on an open callout the real time distance between user location and the annotation I add to the map. Annotation does not change its position.
I add annotations to mapView, using a custom annotation I have defined. No issue here.
On each annotation selected, the callout displays all the information defined in the custom annotation
However, the distance is refreshed in the callout ONLY if I unselect the annotation and reselect it
The distance property is declared as #objc dynamic so it can be observed.
I compute the distance each time the user location change. This part works too.
I cannot figure out what I'm missing to have the callout updated without closing and reopening it.
The code I'm using is what is described here by Rob: Swift -How to Update Data in Custom MKAnnotation Callout?
So my question: is it possible to change realtime a value (observed) in a notificationView callout ? If yes is KVO the best approach ?
In the link below, how would be implemented the mapView viewFor method ?
Any example would be very helpful.
It's my first post here, so please if I did it wrong, let me know and I will provide more information and details.
But my situation is trivial: the standard callout performs Key-Value Observation (KVO) on title and subtitle. (And the annotation view observes changes to coordinate.). But how to display change of values in the current open callout ? That is the think I do not get.
CustomAnnotation class:
class CustomAnnotation: NSObject, MKAnnotation {
#objc dynamic var title: String?
#objc dynamic var subtitle: String?
#objc dynamic var coordinate: CLLocationCoordinate2D
#objc dynamic var distance: CLLocationDistance
var poiColor: String?
var poiPhone: String?
init(title: String, subtitle: String, coordinate: CLLocationCoordinate2D, poiColor: String, poiPhone: String, distance: CLLocationDistance) {
self.title = title
self.subtitle = subtitle
self.coordinate = coordinate
self.poiColor = poiColor
self.poiPhone = poiPhone
self.distance = distance
super.init()
}
}
CustomAnnotationView class:
class CustomAnnotationView: MKMarkerAnnotationView {
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
displayPriority = .required
canShowCallout = true
detailCalloutAccessoryView = createCallOutWithDataFrom(customAnnotation: annotation as? CustomAnnotation)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
removeAnyObservers()
}
override var annotation: MKAnnotation? {
didSet {
removeAnyObservers()
if let customAnnotation = annotation as? CustomAnnotation {
updateAndAddObservers(for: customAnnotation)
}
}
}
private var subtitleObserver: NSKeyValueObservation?
private var distanceObserver: NSKeyValueObservation?
private let subtitleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let distanceLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
}
private extension CustomAnnotationView {
func updateAndAddObservers(for customAnnotation: CustomAnnotation) {
subtitleLabel.text = customAnnotation.subtitle
subtitleObserver = customAnnotation.observe(\.subtitle) { [weak self] customAnnotation, _ in
self?.subtitleLabel.text = customAnnotation.subtitle
}
let locationManager = CLLocationManager()
let theLatitude:CLLocationDegrees = (locationManager.location?.coordinate.latitude)!
let theLongitude:CLLocationDegrees = (locationManager.location?.coordinate.longitude)!
// Get pin location
let pointLocation = CLLocation(latitude: customAnnotation.coordinate.latitude, longitude: customAnnotation.coordinate.longitude)
//Get user location
let userLocation = CLLocation(latitude: theLatitude, longitude: theLongitude)
// Return distance en meters
let distanceFromUser = pointLocation.distance(from: userLocation)
customAnnotation.distance = distanceFromUser*100
distanceLabel.text = String(format: "%.03f", customAnnotation.distance)+" cm"
distanceObserver = customAnnotation.observe(\.distance) { [weak self] customAnnotation, _ in
self?.distanceLabel.text = "\(customAnnotation.distance) cm"
}
}
func removeAnyObservers() {
subtitleObserver = nil
distanceObserver = nil
}
func createCallOutWithDataFrom(customAnnotation: CustomAnnotation?) -> UIView {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = true
view.addSubview(subtitleLabel)
view.addSubview(distanceLabel)
NSLayoutConstraint.activate([
subtitleLabel.topAnchor.constraint(equalTo: view.topAnchor),
subtitleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor),
subtitleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),
subtitleLabel.bottomAnchor.constraint(equalTo: distanceLabel.topAnchor),
distanceLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor),
distanceLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),
distanceLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
if let customAnnotation = customAnnotation {
updateAndAddObservers(for: customAnnotation)
}
return view
}
}
And to finish:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation { return nil }
let annotation = annotation as? CustomAnnotation
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "CustomAnnotation") as? CustomAnnotationView
if annotationView == nil {
annotationView = CustomAnnotationView(annotation: annotation, reuseIdentifier: "CustomAnnotation")
annotationView?.canShowCallout = true
} else {
annotationView?.annotation = annotation
}
return annotationView
}
Thank you.
You would appear to have correctly configured the observers for the subtitle and distance. The problem is that a change in location is not triggering an update to distance. Thus, there is nothing triggering the KVO.
You have an observer for distance, which will trigger an update of the label. But you are not changing distance. You should remove the CLLocationManager code from that routine where you add the observers, and instead create a location manager (not within the annotation view, though) which uses its delegate to update all of the annotation distances, e.g.:
class ViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.distanceFilter = 5
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let currentLocation = locations.last(where: { $0.horizontalAccuracy >= 0 }) else { return }
mapView.annotations
.compactMap { $0 as? CustomAnnotation }
.forEach {
$0.distance = CLLocation(latitude: $0.coordinate.latitude, longitude: $0.coordinate.longitude)
.distance(from: currentLocation)
}
}
}
Obviously, you would remove the CLLocationManager code from updateAndAddObservers.
My second viewcontroller contains mkmapview.. all the time map must to show current location address values in textfields only but when i send values from firstviewcontroller then only it should show firstviewcontroller's address values in its textfields
Initially second viewcontroller textfields showing current location values(like: city, street, etc)
now i am sending first viewcontroller address to secondviewcontroller.. but here secondviewcontroller always showing current location values only.. why?
firstvc code: sending address to 2ndvc:
func btnEditTapped(cell: EditAddressTableViewCell) {
if let indexPath = self.addeditTableview.indexPath(for: cell){
print("edit button address index \(indexPath.row)")
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "ProfileAddressViewController") as! ProfileAddressViewController
var addrLo = addressArray[indexPath.row]
print("in edit total mode address \(addrLo.pincode)")// in console in edit total mode address Optional("530013")
viewController.editPincode = addrLo.pincode
viewController.editStree = addrLo.streetName
viewController.editColony = addrLo.city
self.navigationController?.pushViewController(viewController, animated: true)
}
}
my secondvc code: here i am unable to show firstvc's values why?
class ProfileAddressViewController: UIViewController, CLLocationManagerDelegate, UISearchBarDelegate, DataEnteredDelegate {
var coordinate: CLLocationCoordinate2D = CLLocationCoordinate2D()
let locationManager = CLLocationManager()
var addressModel : ProfileModelUserAddress?
var editAddArray = [String]()
var addressArray = [String]()
#IBOutlet weak var titleNameLabel: UILabel!
#IBOutlet weak var backButton: UIButton!
#IBOutlet weak var pincodeField: UITextField!
#IBOutlet weak var cityField: UITextField!
#IBOutlet weak var streetField: UITextField!
#IBOutlet weak var mapView: MKMapView!
var editPincode: String?
var editStree: String?
var editColony: String?
var isEdit = Bool()
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
if isEdit {
titleNameLabel.text = "Edit Address"
pincodeField.text = editPincode
streetField.text = editStree
colonyField.text = editColony
}
}
#IBAction func backBtnClicked(_ sender: Any) {
self.navigationController?.popViewController(animated: true)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let _: CLLocationCoordinate2D = manager.location?.coordinate else { return }
let userLocation :CLLocation = locations.last! as CLLocation
latitude = userLocation.coordinate.latitude
logitude = userLocation.coordinate.longitude
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(userLocation) { (placemarks, error) in
if (error != nil){
print("error in reverseGeocode")
}
let placemark = placemarks! as [CLPlacemark]
if placemark.count>0{
let placemark = placemarks![0]
print(placemark.locality!)
print(placemark.administrativeArea!)
print(placemark.country!)
let placemarkDictonary: NSDictionary=placemark.addressDictionary as! NSDictionary
self.pincodeField.text=placemarkDictonary["ZIP"] as? String
self.cityField.text=placemarkDictonary["City"] as? String
self.streetField.text=placemarkDictonary["Street"] as? String
}
}
let center = CLLocationCoordinate2D(latitude: latitude!, longitude: logitude!)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
mapView.setRegion(region, animated: true)
let myAnnotation: MKPointAnnotation = MKPointAnnotation()
myAnnotation.coordinate = CLLocationCoordinate2DMake(userLocation.coordinate.latitude, userLocation.coordinate.longitude);
myAnnotation.title = "Current location"
mapView.addAnnotation(myAnnotation)
}
}
please let me know, how to show firstvc's passed values in 2nd vc
Do you set value for the isEdit somewhere else?. If not, the initial value for the isEdit will be false since you are using the initializer of Bool which returns false by default, therefore, your code that initiates values for your text fields inside the if statement will never get run. That's why you don't see the values you've passed from the first VC.
I have a custom MkAnnotation that has a title, subtitle, and extra information associated with it for use on an map view. I am passing this information to another UIViewController. However there seems to be some issue with my coordinate that I have allocated for the pin. The error is highlighted in the setting information of pin below on the line of code "var bridge17pin = MyAnnotation()".
override func viewDidLoad() {
super.viewDidLoad()
//for users location
myLocMgr.desiredAccuracy = kCLLocationAccuracyBest
myLocMgr.requestWhenInUseAuthorization()
myLocMgr.startUpdatingLocation()
myLocMgr.delegate = self
mapView.delegate = self
//coordinate for pin
var bridge17 = CLLocationCoordinate2DMake(53.346061, -6.227379)
//custom MKAnnotation
class MyAnnotation: NSObject, MKAnnotation {
#objc var coordinate: CLLocationCoordinate2D
var EXTRA_INFORMATION: String?
var title: String?
init(coordinate: CLLocationCoordinate2D) {
self.coordinate = coordinate
}
}
//setting information of pin
var bridge17pin = MyAnnotation()
bridge17pin.coordinate = bridge17
bridge17pin.title = "The bridge"
bridge17pin.subtitle = "hello this is the bridge"
bridge17pin.EXTRA_INFORMATION = "this was built in 2010"
mapView.addAnnotation(bridge17pin)
}
Because you have to send the coordinate while you are instantiating. So in the code replace
var bridge17pin = MyAnnotation()
with
var bridge17pin = MyAnnotation(coordinate: bridge17)
And for that subTitle see this link
I did this MapKit-Tutorial
Everything works fine, but how can I add an extra attribut to my pin?
This is my class Car:
import Foundation
import MapKit
class Car: NSObject, MKAnnotation {
let title: String
let subtitle: String
let thirdAttribut: String
let coordinate: CLLocationCoordinate2D
init(title: String, subtitle: String, thirdAttribut: String, coordinate: CLLocationCoordinate2D) {
self.title = title
self.subtitle = subtitle
self.thirdAttribut = thirdAttribut
self.coordinate = coordinate
super.init()
}
var getTitle: String {
return title
}
var getSubtitle: String {
return subtitle
}
var getThirdAttribut: String {
return thirdAttribut
}
I want click on an pin and work with this the "thirdAttribut".
My Main-Class:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
mapView.delegate = self
let car = Car(title: "Title", subtitle: "Subtitle", thirdAttribut: "ThirdAttribut", coordinate: CLLocationCoordinate2D(latitude: 50.906849, longitude: 7.524224) )
mapView.addAnnotation(car)
}
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
//some config stuff
}
func mapView(mapView: MKMapView!, didSelectAnnotationView view: MKAnnotationView!) {
print("Here Comes the Click")
print(view.annotation.title)
print(view.annotation.subtitle)
//view.annotation.thirdAttribut doesn't existis
//How get I my "thirdAttribut"-Attribut?
}
The third-Attribut mustn't appear in my view. It just contains some data for logic-operations.
I hope u understand me, english isn't my mother tongue.
If u know other ways to code what I want then please tell me. :)
Thank u!
An annotation view contains an annotation property. To get that third attribute, you need to cast MKAnnotation to the class you created that adheres to the MKAnnotation protocol, Car.
if let carAnnotation = view.annotation as? Car{
print(carAnnotation.thirdAttrib);
}
I would like to know if anyone can tell me how I can touch a pin on the map in the form of MKPointAnnotations .
I would like to click the pin on the map and go to another view by bringing back the variables of the pin that I have preset .
Can anyone explain me this thing in Swift ?
thanks
Edit with code:
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mappa: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
var location : CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 44.648590, longitude: 10.918794)
let pinAnnotation = PinAnnotation()
pinAnnotation.setCoordinate(location)
self.mappa.addAnnotation(pinAnnotation)
}
class PinAnnotation : NSObject, MKAnnotation {
private var coord: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 0, longitude: 0)
var coordinate: CLLocationCoordinate2D {
get {
return coord
}
}
var title: String = "test"
var subtitle: String = "test"
func setCoordinate(newCoordinate: CLLocationCoordinate2D) {
self.coord = newCoordinate
}
}
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if annotation is PinAnnotation {
let pinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myPin")
pinAnnotationView.pinColor = .Purple
pinAnnotationView.draggable = true
pinAnnotationView.canShowCallout = true
pinAnnotationView.animatesDrop = true
let deleteButton = UIButton.buttonWithType(UIButtonType.Custom) as UIButton
deleteButton.frame.size.width = 44
deleteButton.frame.size.height = 44
deleteButton.backgroundColor = UIColor.redColor()
deleteButton.setImage(UIImage(named: "trash"), forState: .Normal)
pinAnnotationView.leftCalloutAccessoryView = deleteButton
return pinAnnotationView
}
return nil
}
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
if let annotation = view.annotation as? PinAnnotation {
self.mapView.removeAnnotation(annotation)
}
}
}
Several steps are necessary, here are some code snippets to get you started.
First you need a custom class for your pin annotation which holds the data you want to work with.
import MapKit
import Foundation
import UIKit
class PinAnnotation : NSObject, MKAnnotation {
private var coord: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 0, longitude: 0)
private var _title: String = String("")
private var _subtitle: String = String("")
var coordinate: CLLocationCoordinate2D {
get {
return coord
}
}
func setCoordinate(newCoordinate: CLLocationCoordinate2D) {
self.coord = newCoordinate
}
var title: String? {
get {
return _title
}
set (value) {
self._title = value!
}
}
var subtitle: String? {
get {
return _subtitle
}
set (value) {
self._subtitle = value!
}
}
}
Then you need a custom class for your MKMapView which conforms to the MKMapViewDelegate protocol. Implement the method viewForAnnotation there:
import MapKit
import CLLocation
import Foundation
import UIKit
class MapViewController: UIViewController, MKMapViewDelegate {
...
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if annotation is PinAnnotation {
let pinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myPin")
pinAnnotationView.pinColor = .Purple
pinAnnotationView.draggable = true
pinAnnotationView.canShowCallout = true
pinAnnotationView.animatesDrop = true
let deleteButton = UIButton.buttonWithType(UIButtonType.Custom) as UIButton
deleteButton.frame.size.width = 44
deleteButton.frame.size.height = 44
deleteButton.backgroundColor = UIColor.redColor()
deleteButton.setImage(UIImage(named: "trash"), forState: .Normal)
pinAnnotationView.leftCalloutAccessoryView = deleteButton
return pinAnnotationView
}
return nil
}
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
if let annotation = view.annotation as? PinAnnotation {
mapView.removeAnnotation(annotation)
}
}
That gives you something like this:
To add a new annotation to your map use this somewhere in your code:
let pinAnnotation = PinAnnotation()
pinAnnotation.setCoordinate(location)
mapView.addAnnotation(pinAnnotation)
Greate work !!! BUT..
I just copy past this and I had to add a few changes. I'll share with you guys those changes.
import MapKit
import Foundation
import UIKit
class PinAnnotation : NSObject, MKAnnotation {
private var coord: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 0, longitude: 0)
private var _title: String = String("")
private var _subtitle: String = String("")
var title: String? {
get {
return _title
}
set (value) {
self._title = value!
}
}
var subtitle: String? {
get {
return _subtitle
}
set (value) {
self._subtitle = value!
}
}
var coordinate: CLLocationCoordinate2D {
get {
return coord
}
}
func setCoordinate(newCoordinate: CLLocationCoordinate2D) {
self.coord = newCoordinate
}
}
I hope it will help :D