import UIKit
import MapKit
import CoreLocation
class ServisimNeredeViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
var coordinates: [[Double]]!
var names:[String]!
var addresses:[String]!
var phones:[String]!
var locationManager :CLLocationManager = CLLocationManager()
let singleton = Global.sharedGlobal
let point = ServisimAnnotation(coordinate: CLLocationCoordinate2D(latitude: 41.052466 , longitude: 29.132123))
override func viewDidLoad() {
super.viewDidLoad()
coordinates = [[41.052466,29.108976]]// Latitude,Longitude
names = ["Servisiniz Burada"]
addresses = ["Furkan Kutlu"]
phones = ["5321458375"]
self.map.delegate = self
let coordinate = coordinates[0]
point.image = UIImage(named: "direksiyon")
point.name = names[0]
point.address = addresses[0]
point.phone = phones[0]
self.map.addAnnotation(point)
...
}
...
}
I add the coordinate's annotation when the first screen is loaded I update the newly set coordination when the button is pressed. I want instant update of location when button is pressed. How can I do it?
#IBAction func tttesttt(_ sender: Any) {
self.point.coordinate = CLLocationCoordinate2D(latitude: 42.192846, longitude: 29.263417)
}
Does not update the new location when you do the above operation. But the coordination is eliminated and the new one is updated instead I did it like this, but it did not happen again
DispatchQueue.main.async {
self.point.coordinate = CLLocationCoordinate2D(latitude: surucuKordinant.latitude!, longitude: surucuKordinant.longitude!)
}
The likely issue is that your configuration property has not been configured for key-value observing (KVO), which is how the map and/or annotation view become aware of changes of coordinates.
I would ensure that coordinate is KVO-capable by including the dynamic keyword. For more information, see the Key-Value Observing in Using Swift with Cocoa and Objective-C: Adopting Cocoa Design Patterns.
Clearly, we don't have to do all of that observer code in that KVO discussion (as MapKit is doing all of that), but we do at least need to make our annotation KVO-capable. For example, your annotation class might look like:
class ServisimAnnotation: NSObject, MKAnnotation {
dynamic var coordinate: CLLocationCoordinate2D
...
}
The failure to declare coordinate as dynamic will prevent key-value notifications from being posted, and thus changes to the annotation will not be reflected on the map.
Related
I'm trying to get an annotation of something but it gives errors I don't know how to get rid of, does anyone happen to know how to solve this?
This is the code I currently have but it says that value of type 'MKPointAnnotation' has no member 'setCoordinate'. And it says: use of unresolved identifier 'map'.
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController {
#IBOutlet var Map: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
var WCAula = CLLocationCoordinate2DMake(50.3044, 4.5724)
// These are the lattitude and longitude of the toilet across the wooden stairs in the auditorium.
var point1 = MKPointAnnotation()
point1.setCoordinate(WCAula)
point1.title = "WCAula"
map.addAnnotation(point1)
}
}
The map view you have defined starts with a capital M, whereas the map you refer to begins with a lowercase m. Change it so they are both lowercase as that is the convention in Swift.
As far as setting the coordinate, calling setCoordinate() is Objective-C syntax. In Swift you just set the coordinate directly.
point1.coordinate = WCAula
I am trying to learn MapKit and add some MKPolyLine as an overlay. Couple of questions:
What is the difference between MKPolyLine and MKPolyLineView. Which one should be used when?
For the MKPolyLine init method one of the parameter is a generic type(MKMapPoint) of UnsafePointer? Not really sure what is that supposed to mean. Looking up the various SO questions, it seems we are supposed to pass the memory address of the CLLocationCoordinate2D struct as a parameter, but it doesn't work for me.
let testline = MKPolyline()
let coords1 = CLLocationCoordinate2D(latitude: 52.167894, longitude: 17.077399)
let coords2 = CLLocationCoordinate2D(latitude: 52.168776, longitude: 17.081326)
let coords3 = CLLocationCoordinate2D(latitude: 52.167921, longitude: 17.083730)
let testcoords:[CLLocationCoordinate2D] = [coords1,coords2,coords3]
let line = MKPolyline.init(points: &testcoords, count: testcoords.count)
mapView.add(testline)
I keep getting a "& is not allowed passing array value as 'UnsafePointer<MKMapPoint>" argument".
What is wrong here?
1.
MKPolyLine is a class which holds multiple map-coordinates to define the shape of a polyline, very near to just an array of coordinates.
MKPolyLineView is a view class which manages the visual representation of the MKPolyLine. As mentioned in the Kane Cheshire's answer, it's outdated and you should use MKPolyLineRenderer.
Which one should be used when?
You need to use both MKPolyLine and MKPolyLineRenderer as in the code below.
2.
MKPolyLine has two initializers:
init(points: UnsafePointer<MKMapPoint>, count: Int)
init(coordinates: UnsafePointer<CLLocationCoordinate2D>, count: Int)
When you want to pass [CLLocationCoordinate2D] to MKPolyLine.init, you need to use init(coordinates:count:).
(Or you can create an Array of MKMapPoint and pass it to init(points:count:).)
And when you want to pass an immutable Array (declared with let) to UnsafePointer (non-Mutable), you have no need to prefix &.
The code below is actually built and tested with Xcode 9 beta 3:
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let coords1 = CLLocationCoordinate2D(latitude: 52.167894, longitude: 17.077399)
let coords2 = CLLocationCoordinate2D(latitude: 52.168776, longitude: 17.081326)
let coords3 = CLLocationCoordinate2D(latitude: 52.167921, longitude: 17.083730)
let testcoords:[CLLocationCoordinate2D] = [coords1,coords2,coords3]
let testline = MKPolyline(coordinates: testcoords, count: testcoords.count)
//Add `MKPolyLine` as an overlay.
mapView.add(testline)
mapView.delegate = self
mapView.centerCoordinate = coords2
mapView.region = MKCoordinateRegion(center: coords2, span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02))
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
//Return an `MKPolylineRenderer` for the `MKPolyline` in the `MKMapViewDelegate`s method
if let polyline = overlay as? MKPolyline {
let testlineRenderer = MKPolylineRenderer(polyline: polyline)
testlineRenderer.strokeColor = .blue
testlineRenderer.lineWidth = 2.0
return testlineRenderer
}
fatalError("Something wrong...")
//return MKOverlayRenderer()
}
}
Change testCoords to be var instead of let. It needs to be mutable (variable) to be passed in as a pointer.
You're also trying to pass an array of CLLocationCoordinate2Ds into an argument that expects an array of MKMapPoints. Either convert your coordinates into points or use the function that takes an array of coordinates instead.
MKPolyLineView is an old way of rendering lines onto maps, Apple's docs say to use MKPolylineRenderer from iOS 7 and onwards: https://developer.apple.com/documentation/mapkit/mkpolylineview
I am new to iOS and I have a question about finding the current user location. I am reading in Apple documentation:
Displaying the User’s Current Location on the Map Map Kit includes
built-in support for displaying the user’s current location on the
map. To show this location, set the showsUserLocation property of your
map view object to YES. Doing so causes the map view to use Core
Location to find the user’s location and add an annotation of type
MKUserLocation to the map.
The addition of the MKUserLocation annotation object to the map is
reported by the delegate in the same way that custom annotations are.
If you want to associate a custom annotation view with the user’s
location, you should return that view from your delegate object’s
mapView:viewForAnnotation: method. If you want to use the default
annotation view, return nil from that method. To learn more about
adding annotations to a map, see Annotating Maps.
And it sounds great. But then...
import UIKit
import MapKit
class ViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
//set initial location in Honolulu
//let initialLocation = CLLocation(latitude: 21.282778, longitude: -157.829444)
mapView.showsUserLocation = true
let initialLocation = mapView.userLocation.location
centerMapOnLocation(initialLocation)
// let artwork = Artwork(title: "King David Kalakaua", locationName: "Waikiki Gateway Park", discipline: "Sculpture", coordinate: CLLocationCoordinate2D(latitude: 21.283921, longitude: -157.831661))
// mapView.addAnnotation(artwork)
//
// mapView.delegate = self
}
let regionRadius: CLLocationDistance = 1000
func centerMapOnLocation(location: CLLocation){
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate, regionRadius * 2.0, regionRadius * 2.0)
mapView.setRegion(coordinateRegion,animated:true)
}
}
And I have a fatal error: unexpectedly found nil while unwrapping an Optional value. I don't get why. If I set the location manually - of course it is fine. But in the documentation it is written that it will add an annotation on the map. No annotation is added and it crashes. Isn't it possible to get the user coordinates without using the CLLocationManager?
Have you asked permissions to the user to let your app use the location services? The docs at Apple can help you with this. Look at the sample code below to get you started:
private func beginLocationUpdates() {
// Request location access permission
_locationManager!.requestWhenInUseAuthorization()
// Start observing location
_locationManager!.startUpdatingLocation()
}
I would like to ask function annotation to return the coordinate (CLLocationCoordinate2D) for other function to use, here's my partial code:
// ULMap is MapView.
override func viewDidLoad() {
var longPressGR = UILongPressGestureRecognizer(target: self, action: "annotation:")
longPressGR.minimumPressDuration = 1
UImap.addGestureRecognizer(longPressGR)
}
func annotation(gesture: UIGestureRecognizer){
//Coordinate
var touchPoint = gesture.locationInView(self.UImap)
var coordinate = UImap.convertPoint(touchPoint, toCoordinateFromView: self.UImap)
}
I tried this one, but it doesn't work:
func annotation(gesture: UIGestureRecognizer) -> CLLocationCoordinate2D{
//Coordinate
var touchPoint = gesture.locationInView(self.UImap)
var coordinate = UImap.convertPoint(touchPoint, toCoordinateFromView: self.UImap)
return coordinate
}
Is there a way to do this? thanks in advance.
Things like gesture recognizer calls can't return anything because you aren't the one who is calling them. They are getting called by the system, so any return value will propagate back up through code you don't have access to. You should create a class level variable for your coordinate and set that.
so instead of saying
var coordinate = UImap.convertPoint(touchPoint, toCoordinateFromView: self.UImap)
you declare
var coordinate:CLLocationCoordinate2D
at class scope, and then in your function
coordinate = UImap.convertPoint(touchPoint, toCoordinateFromView: self.UImap)
Then coordinate will always be the most recently set coordinate. You can add them to an array if you need to keep track of more than one.
Currently i am trying to use MKLocalSearch to show the informations around user, but the problem is that there is always fatal error: unexpectedly found nil while unwrapping an Optional value. I've tried to make sure that there is no optional value, but still got the error
import UIKit
import MapKit
import CoreLocation
class MapKitViewController: UIViewController {
#IBOutlet weak var MapAround: MKMapView!
let locationManger:CLLocationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
var viewRegion = MKCoordinateRegion(center: MapAround.userLocation.location.coordinate, span: span)
var request = MKLocalSearchRequest()
request.naturalLanguageQuery = "restaurants"
request.region = MKCoordinateRegion(center: MapAround.userLocation.location.coordinate, span: span)
let search = MKLocalSearch(request: request)
search.startWithCompletionHandler {
(response:MKLocalSearchResponse!, error:NSError!) -> Void in
if (error == nil) {
println("searched")
for item in response.mapItems as [MKMapItem]{
println("Item name = \(item.name)")
}
} else {
println(error)
}
}
// Do any additional setup after loading the view.
}
override func viewWillAppear(animated: Bool) {
let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
var viewRegion = MKCoordinateRegion(center: MapAround.userLocation.location.coordinate, span: span)
MapAround.setRegion(viewRegion, animated: true)
}
}
Does anyone know where is the problem?
Thanks!!
You have two implicitly unwrapped optionals, the MKMapView and the userLocation. You might want to check to see if either of these are nil. The map view shouldn't be nil unless the the outlet was not hooked up properly or the view controller was not instantiated properly. The userLocation could conceivably be nil if location services had not yet determined the user's location.
As a general rule, when you first start location services (either through the map's "show user location" feature, or manually starting location services), it takes a little time before the user location is actually determined. As a result, one should be wary of ever trying to use the location directly in viewDidLoad.
Instead, you should implement the MKMapViewDelegate method didUpdateUserLocation (and don't forget to set the delegate of the map view). That function will be called when the user location has been updated, and you should refrain from trying to use the location until that point.
Alternatively, you can start location services of your CLLocationManager and wait for didUpdateLocations (one of the CLLocationManagerDelegate methods). But the concept is the same: Recognize that determining the user's location happens asynchronously, so wait for the OS to tell you that it determined the user location (and one of sufficient accuracy for the purposes of your app).
Having said that, I'd actually be more inclined to implement this search as part of the map view's regionDidChangeAnimated. You probably want to search the visible map, rather than the user's current location. If you happen to be using userTrackingMode of .Follow, it's equivalent, but if you ever contemplate letting the user move the map around (e.g. let the user search where they're going rather than where they current are), the regionDidChangeAnimated probably makes more sense.