I am using swift and I am working on a project, where I have to show a draggable map with changing location. and below the map I have subview and it have a button, on button click the subview will appear and on same button click I will disappear.
But the problem is sometime its working fine some time this view is go down and not coming on screen. and specially when I use button title change code.
class LocationMAP: UIViewController,CLLocationManagerDelegate,MKMapViewDelegate {
#IBOutlet weak var locationLabel: UILabel!
#IBOutlet weak var selectAnyoneButton: UIButton!
#IBOutlet weak var selectingView: UIView!
var changingText:Bool = false
#IBOutlet weak var map: MKMapView!
var locationManger = CLLocationManager()
let geoCoder = CLGeocoder()
var myLocation: CLLocation!
override func viewDidLoad() {
super.viewDidLoad()
self.locationManger.delegate = self
locationManger.desiredAccuracy = kCLLocationAccuracyBest
locationManger.requestWhenInUseAuthorization()
locationManger.startUpdatingLocation()
if( CLLocationManager.authorizationStatus() == CLAuthorizationStatus.AuthorizedWhenInUse ||
CLLocationManager.authorizationStatus() == CLAuthorizationStatus.AuthorizedAlways){
}
self.map.showsUserLocation = true
self.map.delegate = self
self.map.setUserTrackingMode(MKUserTrackingMode.Follow, animated: true)
let location = CLLocationCoordinate2DMake(20.59368, 78.96288)
let span = MKCoordinateSpanMake(0.2, 0.2)
_ = MKCoordinateRegionMake(location, span)
let annotation = MKPointAnnotation()
annotation.coordinate = (location)
selectAnyoneButton.setTitle("Submit", forState: .Normal)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
//MARK:- MapView Delegates
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .Authorized, .AuthorizedWhenInUse:
manager.startUpdatingLocation()
self.map.showsUserLocation = true
default: break
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.myLocation = locations.last! as CLLocation
let userLocation:CLLocation = locations.last!
let long = userLocation.coordinate.longitude
let lat = userLocation.coordinate.latitude
print(long , lat)
// locationManger.stopUpdatingLocation()
self.map.centerCoordinate = myLocation.coordinate
let reg = MKCoordinateRegionMakeWithDistance(myLocation.coordinate, 1500, 1500)
self.map.setRegion(reg, animated: true)
geoCode(myLocation)
}
func geoCode(location : CLLocation!){
geoCoder.cancelGeocode()
self.locationManger.stopUpdatingLocation()
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemark, error) -> Void in
guard let placeMarks = placemark as [CLPlacemark]! else {
return
}
let loc: CLPlacemark = placeMarks[0]
let addressDict : [NSString:NSObject] = loc.addressDictionary as! [NSString: NSObject]
let addrList = addressDict["FormattedAddressLines"] as! [String]
let address = (addrList.joinWithSeparator(", "))
self.locationLabel.text = address
let lat = loc.location!.coordinate.latitude
let long = loc.location!.coordinate.longitude
print(lat , long)
SharedPreferenceManager.sharedInstance.userLatitude = lat
SharedPreferenceManager.sharedInstance.userLongitude = long
SharedPreferenceManager.sharedInstance.userAddress = address
})
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print(error.localizedDescription)
}
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
let location = CLLocation(latitude: mapView.centerCoordinate.latitude, longitude: mapView.centerCoordinate.longitude)
geoCode(location)
self.map.removeAnnotations(mapView.annotations)
let annotation = MKPointAnnotation()
annotation.coordinate = map.centerCoordinate
annotation.title = "title"
annotation.subtitle = "subtitle"
self.map.addAnnotation(annotation)
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = MKPinAnnotationView()
if #available(iOS 9.0, *) {
annotationView.pinTintColor = UIColor.blueColor()
annotationView.center = CGPointMake(160, 200)
} else {
}
return annotationView
}
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, didChangeDragState
newState: MKAnnotationViewDragState, fromOldState oldState: MKAnnotationViewDragState) {
if (newState == MKAnnotationViewDragState.Starting) {
view.dragState = MKAnnotationViewDragState.Dragging
}
else if (newState == MKAnnotationViewDragState.Ending || newState == MKAnnotationViewDragState.Canceling){
view.dragState = MKAnnotationViewDragState.None
}
}
//MARK:- Button Action Methods
#IBOutlet weak var downBtn: UILabel!
#IBAction func chooseButtonAction(sender: AnyObject) {
if (changingText == false) {
let newCenter:CGPoint = CGPointMake(selectingView.center.x, selectingView.center.y - 230)
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(0.55)
selectingView.center = newCenter
UIView.commitAnimations()
selectAnyoneButton.setTitle("Select a service", forState: .Normal)
changingText = true
} else {
let newCenter:CGPoint = CGPointMake(selectingView.center.x, selectingView.center.y + 230)
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(0.55)
selectingView.center = newCenter
UIView.commitAnimations()
selectAnyoneButton.setTitle("Submit", forState: .Normal)
changingText = false
}
}
in button's action methods, add:
super.bringSubviewToFront(UIView)
towards the end.
I am assuming your view in question is a direct child of superview.
Related
I am creating my first IOS app and am having a very difficult time understanding why the CalloutAccessoryControlTapped Control function is not working.
The Info button is appearing on the annotation, but when the user taps on the Info button, nothing happens. See screenshot here: Screenshot of Annotation.
I created a function in the View Controller to add the Callout accessory view and suspect there is something wrong with the function.
Any help would be greatly appreciated!
Here is the Function:
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let annotation = view.annotation as? Fire
let status = "Fire Status: Active"
let distance = "Fire Location: 25 miles away"
let ac = UIAlertController(title: status, message: distance, preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "Close", style: .default))
self.present(ac, animated: true, completion: nil)
}
Here is the code from my View Controller -- updated 4/20/2021:
import MapKit
import UIKit
import CoreLocation
class FireMapViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var FireMapView: MKMapView!
let mapView = MKMapView()
var locationManager = CLLocationManager()
var lat = Double()
var lon = Double()
var fires: [Fire] = []
override func viewDidLoad() {
super.viewDidLoad()
setupMapView()
checkLocationServices()
mapView.register(FireMarkerView.self,forAnnotationViewWithReuseIdentifier:
MKMapViewDefaultAnnotationViewReuseIdentifier)
//Initiate URL Session to get Fire data and add Fire data to the Map Annotations
if let url = URL(string: "https://services3.arcgis.com/T4QMspbfLg3qTGWY/arcgis/rest/services/Active_Fires/FeatureServer/0/query?outFields=*&where=1%3D1&f=geojson") {
URLSession.shared.dataTask(with: url) {data, response, error in
if let data = data {
do {
let features = try MKGeoJSONDecoder().decode(data)
.compactMap { $0 as? MKGeoJSONFeature }
let validWorks = features.compactMap(Fire.init)
self.fires.append(contentsOf: validWorks)
DispatchQueue.main.async {
self.mapView.addAnnotations(self.fires)
}
}
catch let error {
print(error)
}
}
}.resume()
}
}
func setupMapView() {
view.addSubview(mapView)
mapView.translatesAutoresizingMaskIntoConstraints = false
mapView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
mapView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
mapView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
}
func checkLocationServices() {
if CLLocationManager.locationServicesEnabled() {
setupLocationManager()
locationManagerDidChangeAuthorization(locationManager)
} else {
// the user didn't turn it on
}
}
//Get the current location permissions for User
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
switch manager.authorizationStatus {
case .authorizedAlways , .authorizedWhenInUse:
mapView.showsUserLocation = true
followUserLocation()
locationManager.startUpdatingLocation()
break
case .notDetermined , .denied , .restricted:
locationManager.requestWhenInUseAuthorization()
break
default:
break
}
switch manager.accuracyAuthorization {
case .fullAccuracy:
break
case .reducedAccuracy:
break
default:
break
}
}
func setupLocationManager() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
let region = MKCoordinateRegion.init(center: location.coordinate, latitudinalMeters: 160934, longitudinalMeters: 160934)
mapView.setRegion(region, animated: true)
print("Fire Map User Location on Map:", location.coordinate)
// Call stopUpdatingLocation() to stop listening for location updates,
// other wise this function will be called every time when user location changes.
// Need a solution for this.
manager.stopUpdatingLocation()
}
func followUserLocation() {
if let location = locationManager.location?.coordinate {
let region = MKCoordinateRegion.init(center: location, latitudinalMeters: 4000, longitudinalMeters: 4000)
mapView.setRegion(region, animated: true)
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
locationManagerDidChangeAuthorization(locationManager)
}
//Function to display additional Fire data after User selects the Callout Info button on the Annotation
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let annotation = view.annotation as? Fire
print("callout Accessory Tapped!")
let status = "Fire Status: Active"
let distance = "Fire Location: 25 miles away"
let ac = UIAlertController(title: status, message: distance, preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "Close", style: .default))
self.present(ac, animated: true, completion: nil)
}
}
Here is the Class for the Annotation:
import Foundation
import MapKit
class FireMarkerView: MKAnnotationView {
override var annotation: MKAnnotation? {
willSet {
guard let fire = newValue as? Fire else {
return
}
canShowCallout = true
calloutOffset = CGPoint(x: -5, y: 5)
rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
image = fire.image
}
}
}
Here is the Class for the Model:
import Foundation
import MapKit
class Fire: NSObject, MKAnnotation {
let title: String?
let county: String?
let city: String?
let incidentTypeCategory: String?
let coordinate: CLLocationCoordinate2D
let percentContained: Int?
let lastUpdateDateTime: Int?
init(
title: String?,
county: String?,
city: String?,
incidentTypeCategory: String?,
coordinate: CLLocationCoordinate2D,
percentContained: Int?,
lastUpdateDateTime: Int?
) {
self.title = title
self.county = county
self.city = city
self.incidentTypeCategory = incidentTypeCategory
self.coordinate = coordinate
self.percentContained = percentContained
self.lastUpdateDateTime = lastUpdateDateTime
super.init()
}
init?(feature: MKGeoJSONFeature) {
// 1
guard
let point = feature.geometry.first as? MKPointAnnotation,
let propertiesData = feature.properties,
let json = try? JSONSerialization.jsonObject(with: propertiesData),
let properties = json as? [String: Any]
else {
return nil
}
// 2
title = ((properties["IncidentName"] as? String ?? "Unknown") + " Wildfire")
county = ((properties["POOCounty"] as? String ?? "Unknown") + " County")
city = properties["POOCity"] as? String
incidentTypeCategory = properties["IncidentTypeCategory"] as? String
coordinate = point.coordinate
percentContained = properties["PercentContained"] as? Int
lastUpdateDateTime = properties["ModifiedOnDateTime_dt"] as? Int
super.init()
}
var subtitle: String? {
return (county)
}
var image: UIImage {
guard let name = incidentTypeCategory else {
return #imageLiteral(resourceName: "RedFlame")
}
switch name {
case "RX":
return #imageLiteral(resourceName: "YellowFlame")
default:
return #imageLiteral(resourceName: "RedFlame")
}
}
}
Good afternoon Community,
I am having trouble plotting the Poly Line within my application, I already tried calling it in the func method locationManager (_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {}
but nothing is generated, just as I would like each step that I take the polyline to be marked and not at the end of my journey. I leave my code below:
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate,MKMapViewDelegate {
#IBOutlet var Maps: MKMapView!
var locationManager = CLLocationManager()
var coordenadas = CLLocationCoordinate2D()
var startPoint = MKDirections.Request()
var ArrayDeCoordenadas: [CLLocationCoordinate2D] = []
var tap: Int = 0
func getDirections(){
let request = MKDirections.Request()
request.source = MKMapItem.forCurrentLocation()
request.requestsAlternateRoutes = true
let directions = MKDirections(request: request)
directions.calculate { (response, error) in
if error != nil {
print("Error \(error)")
} else {
//self.dispLayRout(response)
var overlays = self.Maps.overlays
self.Maps.removeOverlays(overlays)
for route in response!.routes as! [MKRoute] {
self.Maps.addOverlay(route.polyline,
level: MKOverlayLevel.aboveRoads)
var instructionNumber = 0
for next in route.steps {
instructionNumber += 1
print(next.instructions)
}
}
}
}
}
func alertLocation(title: String, Message:String){
let alert = UIAlertController(title: title, message: Message, preferredStyle: .alert)
let actionAlert = UIAlertAction(title: "Acept", style: .default, handler: nil)
alert.addAction(actionAlert)
self.present(alert, animated: true,completion: nil)
}
func LocalizationInit(){
let autorization = CLLocationManager.authorizationStatus()
switch autorization{
case .notDetermined, .restricted:
locationManager.requestLocation()
break;
case .restricted, .denied:
alertLocation(title: "We have a Error", Message: "You have your localization restricted.")
case .authorizedAlways,.authorizedWhenInUse:
break;
default:
break;
}
}
#IBAction func Share(_ sender: UIButton) {
let compartir = UIActivityViewController(activityItems: ["Share baby" as Any],
applicationActivities: nil)
compartir.popoverPresentationController?.sourceView = self.view
present(compartir,animated: true,completion: nil)
}
#IBAction func recordButton(_ sender: UIButton) {
if sender.isSelected != true {
tap += 1
print("tap -> :\(tap)")
if tap == 1{
let annotation = MKPointAnnotation()
annotation.title = "My route"
annotation.subtitle = "I Hoppe"
annotation.coordinate = coordenadas
let annotation2 = MKPointAnnotation()
annotation2.title = "My ride"
annotation2.subtitle = "I Hoppe"
annotation.coordinate = locationManager.location?.coordinate as! CLLocationCoordinate2D
self.Maps.addAnnotation(annotation)
}else if tap == 2 {
tap = 0
locationManager.stopMonitoringSignificantLocationChanges()
}
}
}
#IBAction func mapOptions(_ sender: UISegmentedControl) {
switch sender.selectedSegmentIndex{
case 0:
Maps.mapType = MKMapType.standard
break;
case 1:
Maps.mapType = MKMapType.satellite
break;
case 2:
Maps.mapType = MKMapType.hybrid
break;
default:
break;
}
}
override func viewDidLoad() {
super.viewDidLoad()
LocalizationInit()
let polyLineMake = MKPolyline(coordinates: ArrayDeCoordenadas, count: ArrayDeCoordenadas.count)
self.Maps.addOverlay(polyLineMake, level: .aboveRoads)
Maps.showsUserLocation = true
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
DispatchQueue.main.async {
self.locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
coordenadas = manager.location?.coordinate as! CLLocationCoordinate2D
ArrayDeCoordenadas.append(locations.last!.coordinate)
print("Array de coordenadas : -> \(ArrayDeCoordenadas)")
let region = MKCoordinateRegion(center: locations.last!.coordinate, latitudinalMeters: 200,
longitudinalMeters: 200)
self.Maps.setRegion(region, animated: false)
let distancia = locations.distance(from: Int(manager.location!.coordinate.latitude), to:
Int((locations.last?.coordinate.latitude)!))
print("distancia \(distancia)")
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation{
return nil
}
let pin = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "My personal pin")
pin.pinTintColor = UIColor.green
return pin
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKPolyline{
let polyline = overlay
let polyLineRender = MKPolylineRenderer(overlay: polyline)
print(" se esta generando \(polyline)")
polyLineRender.strokeColor = UIColor.red
polyLineRender.lineWidth = 6.0
return polyLineRender
}
print(" no se esta generando")
return MKPolylineRenderer()
} }
I have read the documentation a bit and apparently my code is fine, if someone could help me, it would be of great help! Like someone if you have any method that can help me to know when I did and how many steps I walked since I did not see that CLLocation had an option for that type of information.
the problem is not set Map delegate object, also I made some changes to add polyline overlay. Simple but it is working. Good luck.
Maps.delegate = self
Code file:
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate,MKMapViewDelegate {
#IBOutlet var Maps: MKMapView!
var locationManager = CLLocationManager()
var coordenadas = CLLocationCoordinate2D()
var startPoint = MKDirections.Request()
var ArrayDeCoordenadas: [CLLocationCoordinate2D] = []
var tap: Int = 0
var journeyPolyline: MKPolyline?
func getDirections(){
let request = MKDirections.Request()
request.source = MKMapItem.forCurrentLocation()
request.requestsAlternateRoutes = true
let directions = MKDirections(request: request)
directions.calculate { (response, error) in
if error != nil {
print("Error \(error)")
} else {
//self.dispLayRout(response)
var overlays = self.Maps.overlays
self.Maps.removeOverlays(overlays)
for route in response!.routes as! [MKRoute] {
self.Maps.addOverlay(route.polyline,
level: MKOverlayLevel.aboveRoads)
var instructionNumber = 0
for next in route.steps {
instructionNumber += 1
print(next.instructions)
}
}
}
}
}
func alertLocation(title: String, Message:String){
let alert = UIAlertController(title: title, message: Message, preferredStyle: .alert)
let actionAlert = UIAlertAction(title: "Acept", style: .default, handler: nil)
alert.addAction(actionAlert)
self.present(alert, animated: true,completion: nil)
}
func LocalizationInit(){
let autorization = CLLocationManager.authorizationStatus()
switch autorization{
case .notDetermined, .restricted:
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
break;
case .restricted, .denied:
alertLocation(title: "We have a Error", Message: "You have your localization restricted.")
case .authorizedAlways,.authorizedWhenInUse:
locationManager.startUpdatingLocation()
break;
default:
break;
}
}
#IBAction func Share(_ sender: UIButton) {
let compartir = UIActivityViewController(activityItems: ["Share baby" as Any],
applicationActivities: nil)
compartir.popoverPresentationController?.sourceView = self.view
present(compartir,animated: true,completion: nil)
}
#IBAction func recordButton(_ sender: UIButton) {
if sender.isSelected != true {
tap += 1
print("tap -> :\(tap)")
if tap == 1{
let annotation = MKPointAnnotation()
annotation.title = "My route"
annotation.subtitle = "I Hoppe"
annotation.coordinate = coordenadas
let annotation2 = MKPointAnnotation()
annotation2.title = "My ride"
annotation2.subtitle = "I Hoppe"
annotation.coordinate = locationManager.location?.coordinate as! CLLocationCoordinate2D
self.Maps.addAnnotation(annotation)
}else if tap == 2 {
tap = 0
locationManager.stopMonitoringSignificantLocationChanges()
}
}
}
#IBAction func mapOptions(_ sender: UISegmentedControl) {
switch sender.selectedSegmentIndex{
case 0:
Maps.mapType = MKMapType.standard
break;
case 1:
Maps.mapType = MKMapType.satellite
break;
case 2:
Maps.mapType = MKMapType.hybrid
break;
default:
break;
}
}
override func viewDidLoad() {
super.viewDidLoad()
LocalizationInit()
// let polyLineMake = MKPolyline(coordinates: ArrayDeCoordenadas, count: ArrayDeCoordenadas.count)
// self.Maps.addOverlay(polyLineMake, level: .aboveRoads)
Maps.showsUserLocation = true
Maps.delegate = self
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
coordenadas = locations.first!.coordinate as! CLLocationCoordinate2D
ArrayDeCoordenadas.append(locations.last!.coordinate)
print("Array de coordenadas : -> \(ArrayDeCoordenadas)")
let region = MKCoordinateRegion(center: locations.last!.coordinate, latitudinalMeters: 200,
longitudinalMeters: 200)
self.Maps.setRegion(region, animated: false)
let distancia = locations.distance(from: Int(manager.location!.coordinate.latitude), to:
Int((locations.last?.coordinate.latitude)!))
print("distancia \(distancia)")
let polyline = MKPolyline(coordinates: ArrayDeCoordenadas, count: ArrayDeCoordenadas.count)
Maps.addOverlay(polyline)
//remove old polyline
if let oldPolyline = journeyPolyline {
Maps.removeOverlay(oldPolyline)
}
journeyPolyline = polyline
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation{
return nil
}
let pin = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "My personal pin")
pin.pinTintColor = UIColor.green
return pin
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKPolyline{
let polyline = overlay
let polyLineRender = MKPolylineRenderer(overlay: polyline)
print(" se esta generando \(polyline)")
polyLineRender.strokeColor = UIColor.red
polyLineRender.lineWidth = 6.0
return polyLineRender
}
print(" no se esta generando")
return MKPolylineRenderer()
}
}
I'm getting this error "Value of type 'UIGestureRecognizer' has no member 'numberOfTapsRequired' with my code as I'm adding double tap, I read many other posts on the subject and I can't find where is the problem. Can you spot what I'm doing wrong in the addDoubleTap function? This is the entire code
import UIKit
import MapKit
import CoreLocation
class MapViewController: UIViewController, MKMapViewDelegate, UIGestureRecognizerDelegate {
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var dropPinButton: UIButton!
#IBOutlet weak var centerMApButton: UIButton!
var pin: AnnotationPinn!
// variables that hold values for selected icon, to be used for displaying the pin
var dataReceived: String?
// udemy location manager couse
var locationManager = CLLocationManager()
let authorizationStatus = CLLocationManager.authorizationStatus()
let regionRadius: Double = 1000.0
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
locationManager.delegate = self
configureLocationServices()
setCoordinates()
// addDoubleTap()
let latitude: Double = setCoordinates().lat //44.498955
let longitude: Double = setCoordinates().lon //11.327591
let title: String? = dataReceived
var subtitle: String? = dataReceived
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let region = MKCoordinateRegionMakeWithDistance(coordinate, 1000, 1000)
mapView.setRegion(region, animated: true)
// title may be to be taken from iconNames[indexpath.row]
pin = AnnotationPinn(title: "", subtitle: "", coordinate: coordinate)
}
func addDoubleTap() { //not finding numberOfTapsRequired
let doubleTap = UIGestureRecognizer(target: self, action: #selector(MapViewController.dropPin))
doubleTap.numberOfTapsRequired = 2
doubleTap.delegate = self
mapView.addGestureRecognizer(doubleTap)
}
func centerMapOnLocation() {
guard let coordinate2 = locationManager.location?.coordinate else {
return
}
let coordinateRegion = MKCoordinateRegionMakeWithDistance(coordinate2, regionRadius * 2.0, regionRadius * 2.0)
mapView.setRegion(coordinateRegion, animated: true)
}
//custom pin image , named: iconsImages[indexPath.row]
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = MKAnnotationView(annotation: pin, reuseIdentifier: "strada chiusa")
//added if statement for displaying user location blue dot
if annotation is MKUserLocation{
return nil
} else {
annotationView.image = UIImage(named: dataReceived!) // here choose the image to load
let transform = CGAffineTransform(scaleX: 0.22, y: 0.22)
annotationView.transform = transform
return annotationView
}
}
// get coordinates into variables for the dropPin()
func setCoordinates() -> ( lat: Double, lon:Double) {
let location = locationManager.location?.coordinate
let lat:Double = (location?.latitude)!
let lon:Double = (location?.longitude)!
print(lat,lon)
return (lat, lon)
}
func dropPin() {
// print("3\(String(describing: dataReceived))")
mapView.addAnnotation(pin)
}
#IBAction func dropPinButton(_ sender: Any) {
performSegue(withIdentifier: "chooseIconSegue", sender: self)
}
#IBAction func centerMapButton(_ sender: Any) {
if authorizationStatus == .authorizedAlways || authorizationStatus == .authorizedWhenInUse{
centerMapOnLocation()
}
}
#IBAction func unwindHere(sender:UIStoryboardSegue) { // datas coming back
if let sourceViewController = sender.source as? IconsViewController {
// MyVariables.dataReceived = sourceViewController.dataPassed
dataReceived = sourceViewController.dataPassed
// title = sourceViewController.dataPassed
print(dataReceived!)
dropPin()
mapView.addAnnotation(pin)
}
}
func configureLocationServices() {
if authorizationStatus == .notDetermined{
locationManager.requestAlwaysAuthorization()
} else {
return
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
centerMapOnLocation()
}
}
extension MapViewController: CLLocationManagerDelegate{
}
UIGestureRecognizer doesn't have a property called numberOfTapsRequired, this is why you're getting the compilation error.
What you're after is UITapGestureRecognizer.
I'm designing an app which has the a feature to let user add their own pin. Recently, I found my app has a memory leak with the title of "ContiguousArrayStorage". I debugged a little and figured out it was a problem with my for loop in my viewdidload, but I am not sure how to fix the leak.
Here is my code:
#IBOutlet weak var AppleMap: MKMapView!
#IBOutlet weak var LogoutOutlet: UIButton!
#IBOutlet weak var OutletforAddPokemon: UIButton!
#IBOutlet weak var FindLocationOutlet: UIButton!
#IBOutlet weak var FilterOutlet: UIButton!
let locationManager = CLLocationManager()
let calendar: NSCalendar! = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
var increment:Int = 0
var currentLoc:CLLocationCoordinate2D?
var centerMap = true
let StoringPin = FIRDatabase.database().reference().child("locations")
var Date = NSDate()
override func viewDidLoad() {
super.viewDidLoad()
AppleMap.delegate = self
StoringPin.observeEventType(.Value, withBlock: {
snapshot in
if snapshot.value is NSNull {
return
}
let val = snapshot.value as! [String : [String : AnyObject]]
for key in val.keys {
let latitudedata = val[key]!["latitude"] as! Double
let longitudedata = val[key]!["longitude"] as! Double
let namedata = val[key]!["name"] as! String
let Username = val[key]!["Username"]
as! String
let DATE = val[key]!["Date"]
as! String
let NumberOflikesforuser = val[key]!["Likes"] as! Int
let NumberOfDislikesforuser = val[key]!["Dislikes"] as! Int
let coord = CLLocationCoordinate2D(latitude: latitudedata, longitude: longitudedata)
let artwork = Capital(title: "\(namedata)", coordinate: coord, info: "HI", username: Username, NumofLikes: NumberOflikesforuser,NumofDisLikes: NumberOfDislikesforuser, UIDSTring: UID, date: DATE, color: MKPinAnnotationColor.Green)
artwork.subtitle = DATE
print("k")
let permastringforemail:String = Username
print(++self.increment)
print(UID)
print(permastringforemail)
stringforemail = permastringforemail
Arrayforpins.append(artwork)
self.AppleMap.addAnnotation(Arrayforpins[Arrayforpins.count - 1])
for Capital in Arrayforpins {
self.AppleMap.addAnnotation(Capital)
}
}
})
print(LogoutOutlet)
LogoutOutlet.layer.cornerRadius = 4
FindLocationOutlet.layer.cornerRadius = 4
OutletforAddPokemon.layer.cornerRadius = 4
//FilterOutlet.layer.cornerRadius = 4
self.locationManager.requestAlwaysAuthorization()
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
let annotationView = MKAnnotationView()
let detailButton: UIButton = UIButton.init(type: .DetailDisclosure) as UIButton
annotationView.rightCalloutAccessoryView = detailButton
}
let regionRadius: CLLocationDistance = 1000
func centerMapOnLocation(location: CLLocation) {
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,
regionRadius * 2.0, regionRadius * 2.0)
AppleMap.setRegion(coordinateRegion, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse {
AppleMap.showsUserLocation = true
} else {
locationManager.requestWhenInUseAuthorization()
}
print("update")
currentLoc = locValue
if centerMap {
centerMap = false
centerMapOnLocation(CLLocation(latitude: currentLoc!.latitude, longitude: currentLoc!.longitude))
}
}
#IBAction func SendtoSelector(sender: AnyObject) {
self.performSegueWithIdentifier("SeguetoSelector", sender: self)
}
#IBAction func FilterFunc(sender: AnyObject) {
self.performSegueWithIdentifier("SeguetoFilter", sender: self)
}
#IBAction func FindLocation(sender: AnyObject) {
centerMapOnLocation(CLLocation(latitude: currentLoc!.latitude, longitude: currentLoc!.longitude))
}
#IBAction func Logout(sender: AnyObject) {
LO = true
self.performSegueWithIdentifier("BacktoLoginScreen", sender: self)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
locationManager
if IndexBoolean == true{
//let annotationView = MKPinAnnotationView()
print("Hi")
let artwork = Capital(title: "\(pickerDataSource[chosenindex])", coordinate: CLLocationCoordinate2D(latitude: currentLoc!.latitude, longitude: currentLoc!.longitude), info: "HEY", username: stringforemail, NumofLikes: NumberOfLikes, NumofDisLikes: NumberOfDislike, UIDSTring: UID, date: stringfordate2, color: MKPinAnnotationColor.Green)
//print(now)
print(chosenindex)
artwork.title = "\(pickerDataSource[chosenindex])"
//artwork.subtitle = stringfordate
AppleMap.addAnnotation(artwork)
//annotationView.pinColor = artwork.Green
Arrayforpins.append(artwork)
print(stringforemail)
AppleMap.addAnnotation(Arrayforpins[Arrayforpins.count - 1])
for Capital in Arrayforpins{
AppleMap.addAnnotation(Capital)
}
IndexBoolean = false
var formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
formatter.timeZone = NSTimeZone(abbreviation: "Central Time")
var utcTimeZoneSTR = formatter.stringFromDate(Date)
stringfordate = "\(utcTimeZoneSTR)"
let uid = NSUUID().UUIDString
UID = uid
StoringPin.child(uid).setValue(["name" : pickerDataSource[chosenindex],
"latitude" : currentLoc!.latitude,
"longitude" : currentLoc!.longitude,"Array Position" : chosenindex,"Username": stringforemail, "Likes": NumberOfLikes2, "Dislikes":
NumberOfDislike, "UID": UID, "Date": stringfordate])
if FilterBoolean == true {
print("b")
if FilterDataSource[Intforfilter] != stringforname {
print("k")
//self.AppleMap.viewForAnnotation(artwork)?.hidden = true
FilterBoolean == false
}
//else {
// print("m")
// self.AppleMap.removeAnnotation(artwork)
//
// }
FilterBoolean == false
}
}
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "Capital"
print(++increment)
if annotation.isKindOfClass(Capital.self) {
print("CAPITAL")
if let annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier) {
annotationView.annotation = annotation
return annotationView
} else {
let annotationView = MKPinAnnotationView(annotation:annotation, reuseIdentifier:identifier)
annotationView.enabled = true
annotationView.canShowCallout = true
//annotationView.pinColor = MKPinAnnotationColor.Green
let btn = UIButton(type: .DetailDisclosure)
annotationView.rightCalloutAccessoryView = btn
return annotationView
}
}
return nil
}
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let capital = view.annotation as! Capital
let placeName = capital.title
let placeInfo = capital.info
let UserName = capital.username
stringforname = view.annotation!.title!!
coords = view.annotation!.coordinate
stringforemail = capital.username
stringfordate2 = capital.date
NumberOfLikes2 = capital.NumofLikes
UID = capital.UIDSTring
print(stringforname)
print(UID)
self.performSegueWithIdentifier("SegueToInfo", sender: self)
}
I note you're not taking care to specify how to capture self in your closure. When in a disposable object context (a UIViewController) and an async closure you always have to worry about this. You need to capture it as weak or unowned. Are you familiar with closure capture lists?
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID56
The pattern I use is to capture self weakly and then bail if self has become nil, e.g. the UIViewController has been dismissed and harvested. If it is still around, I temporarily capture it strongly with a local variable in a guard...let statement.
withBlock: { [weak self] (snapshot) in
guard let s = self else {
print("Callback called after view unloaded")
return
}
// now use 's' instead of 'self'
...
}
Can't say this is the only problem because we don't have the declarations for your helper classes. For example we don't know what mischief Capital is up to.
By the way, just be clear that your for loop does not run at viewDidLoad(). You're merely registering a callback that only reacts if and when the .Value event occurs on the StoringPin object.
I've been having trouble figuring out how to pass a custom variable in a map pin to another view controller in Swift. I know that passing the coordinates, title, and subtitle are available when you addAnnotation. I would like to try and pass a custom variable but hidden. Is there such a thing? Below I am getting the users location, mapping it, dropping a pin of a couple locations nearby with annotations which goes to another view controller and passes just the title and subtitle. Any insight is greatly appreciated.
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
var mappedCity = String()
var mappedState = String()
var manager = CLLocationManager()
var annotation:MKAnnotation!
var error:NSError!
var pointAnnotation:MKPointAnnotation!
var pinAnnotationView:MKPinAnnotationView!
var selectedAnnotation: MKPointAnnotation!
private var mapChangedFromUserInteraction = false
#IBOutlet var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.delegate = self
self.navigationItem.titleView = searchController.searchBar
self.definesPresentationContext = true
if CLLocationManager.locationServicesEnabled(){
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0]
let latitude = userLocation.coordinate.latitude
let longitude = userLocation.coordinate.longitude
let latDelta:CLLocationDegrees = 0.05
let lonDelta:CLLocationDegrees = 0.05
let span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, lonDelta)
let location:CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(location, span)
self.mapView.setRegion(region, animated: true)
self.mapView.showsUserLocation = true
CLGeocoder().reverseGeocodeLocation(userLocation) { (placemarks, error) in
if (error != nil){
print(error)
}else {
if let p = placemarks?[0]{
let locality = p.locality ?? ""
let administrativeArea = p.administrativeArea ?? ""
self.mappedCity = String(locality)
self.mappedState = String(administrativeArea)
self.parseJSON("\(locality)", state: "\(administrativeArea)")
}
}
}
self.manager.stopUpdatingLocation()
}
func parseJSON(city: String, state: String){
let passedCity = city
let passedState = state
let escapedCity = passedCity.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
let escapedState = passedState.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
let url = NSURL(string:"http://www.API.com/api.php?city=\(escapedCity)&stateAbv=\(escapedState)")!
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) { (items, response, error) -> Void in
if error != nil {
print(error)
}else {
if let items = items {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(items, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
if jsonResult.count > 0 {
if let datas = jsonResult["data"] as? NSArray{
for data in datas{
if let title = data["title"] as? String {
if let street = data["street"] as? String {
if let city = data["city"] as? String {
if let stateAbv = data["stateAbv"] as? String {
if let zip = data["zip"] as? String {
self.geoAddress("\(title)", street: "\(street)", city: "\(city)", state: "\(stateAbv)", zip: "\(zip)")
}
}
}
}
}
}
}
}
} catch{}
}
}
}
task.resume()
}
func geoAddress(title: String, street: String, city: String, state: String, zip: String){
let storeName = "\(title)"
let location = "\(street) \(city) \(state) \(zip)"
let geocoder = CLGeocoder();
geocoder.geocodeAddressString(location, completionHandler: {(placemarks: [CLPlacemark]?, error: NSError?) -> Void in
if (error != nil) {
print("Error \(error!)")
} else if let placemark = placemarks?[0] {
let coordinates:CLLocationCoordinate2D = placemark.location!.coordinate
let pointAnnotation:MKPointAnnotation = MKPointAnnotation()
pointAnnotation.coordinate = coordinates
pointAnnotation.title = storeName
pointAnnotation.subtitle = location
self.mapView.addAnnotation(pointAnnotation)
}
})
}
private func mapViewRegionDidChangeFromUserInteraction() -> Bool {
let view: UIView = self.mapView.subviews[0] as UIView
// Look through gesture recognizers to determine whether this region change is from user interaction
if let gestureRecognizers = view.gestureRecognizers {
for recognizer in gestureRecognizers {
if( recognizer.state == UIGestureRecognizerState.Began || recognizer.state == UIGestureRecognizerState.Ended ) {
return true
}
}
}
return false
}
func mapView(mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
mapChangedFromUserInteraction = mapViewRegionDidChangeFromUserInteraction()
if (mapChangedFromUserInteraction) {
// user changed map region
}
}
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
if (mapChangedFromUserInteraction) {
// user changed map region
let center = mapView.centerCoordinate
let mapLatitude = center.latitude
let mapLongitude = center.longitude
let locationmove = CLLocation(latitude: mapLatitude, longitude: mapLongitude)
CLGeocoder().reverseGeocodeLocation(locationmove) { (placemarks, error) in
if (error != nil){
print(error)
}else {
if let p = placemarks?[0]{
let locality = p.locality ?? ""
let administrativeArea = p.administrativeArea ?? ""
self.mappedCity = String(locality)
self.mappedState = String(administrativeArea)
self.parseJSON("\(locality)", state: "\(administrativeArea)")
}
}
}
}
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView?.animatesDrop = false
pinView?.canShowCallout = true
pinView?.draggable = true
pinView?.pinTintColor = UIColor.greenColor()
let rightButton: AnyObject! = UIButton(type: UIButtonType.DetailDisclosure)
pinView?.rightCalloutAccessoryView = rightButton as? UIView
}
else {
pinView?.annotation = annotation
}
return pinView
}
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
selectedAnnotation = view.annotation as? MKPointAnnotation
performSegueWithIdentifier("Details", sender: self)
}
}
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, didChangeDragState newState: MKAnnotationViewDragState, fromOldState oldState: MKAnnotationViewDragState) {
if newState == MKAnnotationViewDragState.Ending {
let droppedAt = view.annotation?.coordinate
print(droppedAt)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "Details"){
let myDetails = segue.destinationViewController as! DetailViewController
myDetails.mytitle = selectedAnnotation.title
myDetails.mysubtitle = selectedAnnotation.subtitle
}
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
}
}
Subclass the "MKPointAnnotation" class and add your custom property in it.
class MyAnnotation : MKPointAnnotation {
var customProperty : String?
}
And you can use MyAnnotation instead of MKPointAnnotation. Like following
let pointAnnotation:MyAnnotation = MyAnnotation()
pointAnnotation.coordinate = coordinates
pointAnnotation.title = storeName
pointAnnotation.subtitle = location
pointAnnotation.customProperty = "your value"
self.mapView.addAnnotation(pointAnnotation)