Update GMSMarker location as realtime message received Swift 3.1 - ios

I am new to swift. I am working on an app which shows markers moving on location update when realtime message is received.
View and Steps
Home (Map Screen which will show tracking results) and Search Button
On search button click, a new search view has been displayed. User
types and search
On search result view if found, a table view is shown. User can
select multiple to subscribe and "Add to tracking list"
On "Add to tracking list", app subscribe on realtime api and should start displaying results (on first view - Home) as message received.
Issue:
I am using delegate to share data (method: messageReceived) from MesssagingProcessor class to FirstViewController. Problem is "messageReceived" is not called in MesssagingProcessor class because delegate is nil.
To go back to first, I used following code
self.navigationController?.popToRootViewController(animated: true)
To subscribe,
channel.subscribe { message in
print(message.name ?? Constant.NIL_DEFAULT_TEXT)
print(message.data ?? Constant.NIL_DEFAULT_TEXT)
self.messageProcessor.process(message: message)
}
return true
I am using delegate to share data between classes and viewcontrollers
Delegate Class:
protocol MesssagingProcessorDelegate {
func messageReceived(msg: MessageMO)
}
class MesssagingProcessor {
//MAR: Properties
var delegate: MesssagingProcessorDelegate?
var info = ""
//MARK: Constructor
init() {
info = "I am set"
}
//MARK: Functions
func process(message: <MessageTypeHere>) {
let msg = MessageMO(dataString: message.data as! String, colString: Constant.EP.C_MSG)
if MessagingHelper.validMessage(msg: msg) {
if msg.type == Constant.TYPE_MSG_LOC {
if msg.location != nil {
delegate?.messageReceived(msg: msg)
}
else {
print("No location found in Location Message. Message: \(String(describing: message.data))")
}
}
}
}
}
Home View Controller:
import UIKit
import GoogleMaps
import UserNotifications
class FirstViewController: UIViewController, MesssagingProcessorDelegate {
//MARK: Properties
let messsagingProcessor: MesssagingProcessor = MesssagingProcessor()
var bounds = GMSCoordinateBounds()
var mapView: GMSMapView? = nil
//MARK: Events
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton.init(type: .custom)
button.setImage(UIImage.init(named: "logo"), for: UIControlState.normal)
button.addTarget(self, action:#selector(showTrackeeOptions), for: UIControlEvents.touchUpInside)
button.frame = CGRect.init(x: 0, y: 0, width: 30, height: 30) //CGRectMake(0, 0, 30, 30)
let barButton = UIBarButtonItem.init(customView: button)
self.navigationItem.leftBarButtonItem = barButton
let camera = GMSCameraPosition.camera(withLatitude: 53.34624,
longitude: -6.24134, zoom: 6)
mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
mapView!.isMyLocationEnabled = true
mapView!.settings.setAllGesturesEnabled(true)
mapView!.delegate = self
self.view = mapView!
//
let marker = GMSMarker()
marker.position = CLLocationCoordinate2DMake(-33.86, 151.20)
marker.title = "TEST"
marker.snippet = Generic.toImageURL(name: "_demo_bus.png", category: Constant.TYPE_IMG_MARKER, type: Constant.TYPE_USER) + "^TestTest Test <-> Demo Bus^Available^2.3 km/h^1493729075106^10^0^333"
marker.map = mapView!
messsagingProcessor.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: Events
//MARK: MesssagingProcessorDelegate
func messageReceived(msg: MessageMO) {
let mk = MarkerMO(msg: msg).add()
var marker: GMSMarker
if mk != nil {
let imgURL = Generic.toImageURL(name: mk!.iconMarker, category: Constant.TYPE_IMG_MARKER, type: Constant.TYPE_USER)
var sharedInfo = imgURL
sharedInfo += Constant.SEPARATOR_LIST_ITEM
sharedInfo += mk!.name! + Constant.SEPARATOR_LIST_ITEM
sharedInfo += mk!.status + Constant.SEPARATOR_LIST_ITEM
sharedInfo += mk!.speed + Constant.SEPARATOR_LIST_ITEM
sharedInfo += String(mk!.time) + Constant.SEPARATOR_LIST_ITEM
sharedInfo += String(mk!.interval)
if mk!.gMarker != nil {
marker = GMSMarker()
} else {
marker = mk!.gMarker!
}
CATransaction.begin()
CATransaction.setAnimationDuration(1.0)
marker.position = CLLocationCoordinate2D(latitude: (mk!.location!.latitude), longitude: (mk!.location!.longitude))
marker.icon = Generic.toImageByURL(url: imgURL, width: Constant.ICON_DEFAULT_MARKER_WIDTH, height: Constant.ICON_DEFAULT_MARKER_HEIGHT, defaultImage: "defaultmarker")
marker.title = mk!.name
marker.snippet = sharedInfo
marker.map = self.mapView
CATransaction.commit()
mk!.updateGMarker(marker: marker)
self.bounds = self.bounds.includingCoordinate(marker.position)
}
else {
//TODO: Error in marker generation
}
}
}
extension FirstViewController: GMSMapViewDelegate {
//For Info WindowIndow
func mapView(_ mapView: GMSMapView, markerInfoWindow marker: GMSMarker) -> UIView? {
let infoWindow = Bundle.main.loadNibNamed("LocMarkerInfoWindow", owner: self.view, options: nil)!.first! as! LocMarkerInfoWindowController
print(marker.snippet!)
let info = marker.snippet!.components(separatedBy: Constant.SEPARATOR_LIST_ITEM)
infoWindow.iconMarker.image = Generic.toImageByURL(url: info[0], width: Constant.ICON_INFOWIN_MARKER_WIDTH, height: -1, defaultImage: "defaultmarker")
infoWindow.name.text = info[1]
infoWindow.status.text = info[2]
infoWindow.speed.text = info[3]
infoWindow.lastUpdatedOn.text = Double(info[4])?.getDateStringFromTimeStamp()
infoWindow.interval.text = info[5]
return infoWindow
}
}
I will really appreciate any help regarding this. Thank you

I have resolved the issue by declaring messsagingProcessor as global and using it in view controller and class.
let messsagingProcessor: MesssagingProcessor = MesssagingProcessor()
Previously, I was declaring and using different objects in view controller class and client class.
I hope it may help someone. Thanks anyway

Related

Getting a nil in Swift 3?

I am creating an app where I have annotation view showing that when you click it shows on the DetailsViewController that annotation data. However, I get "Name", and "Address" data but for phone Number I am getting set as nil. So, if you guys can see my code & help me solve it, I will be appreciated it.
Here is my code:
import UIKit
import MapKit
protocol UserLocationDelegate {
func userLocation(latitude :Double, longitude :Double)
}
class NearMeMapViewController: ARViewController, ARDataSource, MKMapViewDelegate, CLLocationManagerDelegate {
var nearMeIndexSelected = NearMeIndexTitle ()
var locationManager : CLLocationManager!
var nearMeARAnnotations = [ARAnnotation]()
var nearMeRequests = [NearMeRequest]()
var delegate : UserLocationDelegate!
var place: Place?
override func viewDidLoad() {
super.viewDidLoad()
self.title = nearMeIndexSelected.indexTitle
self.locationManager = CLLocationManager ()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.distanceFilter = kCLHeadingFilterNone
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
self.dataSource = self
self.headingSmoothingFactor = 0.05
self.maxVisibleAnnotations = 30
getNearMeIndexSelectedLocation()
}
func getNearMeIndexSelectedLocation()
{
let nearMeRequest = MKLocalSearchRequest()
nearMeRequest.naturalLanguageQuery = nearMeIndexSelected.indexTitle
let nearMeregion = MKCoordinateRegionMakeWithDistance(self.locationManager.location!.coordinate, 250, 250)
nearMeRequest.region = nearMeregion
let nearMeSearch = MKLocalSearch(request: nearMeRequest)
nearMeSearch.start { (response : MKLocalSearchResponse?, error :Error?) in
for requestItem in (response?.mapItems)! {
let nearMeIndexRequest = NearMeRequest ()
nearMeIndexRequest.name = requestItem.name
nearMeIndexRequest.coordinate = requestItem.placemark.coordinate
nearMeIndexRequest.address = requestItem.placemark.addressDictionary?["FormattedAddressLines"] as! [String]
nearMeIndexRequest.street = requestItem.placemark.addressDictionary?["Street"] as! String!
nearMeIndexRequest.city = requestItem.placemark.addressDictionary?["City"] as! String
nearMeIndexRequest.state = requestItem.placemark.addressDictionary?["State"] as! String
nearMeIndexRequest.zip = requestItem.placemark.addressDictionary?["ZIP"] as! String
self.nearMeRequests.append(nearMeIndexRequest)
print(requestItem.placemark.name)
}
for nearMe in self.nearMeRequests {
let annotation = NearMeAnnotation(nearMeRequest: nearMe)
self.nearMeARAnnotations.append(annotation)
self.setAnnotations(self.nearMeARAnnotations)
}
}
}
func ar(_ arViewController: ARViewController, viewForAnnotation: ARAnnotation) -> ARAnnotationView {
let annotationView = NearMeARAnnotationView(annotation: viewForAnnotation)
// annotationView.delegate = self
annotationView.frame = CGRect(x: 0, y: 0, width: 150, height: 50)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.tapBlurButton(_:)))
annotationView.addGestureRecognizer(tapGesture)
return annotationView
}
func tapBlurButton(_ sender: UITapGestureRecognizer) {
if let annotationView = sender.view as? NearMeARAnnotationView {
if let detailsVc = storyboard?.instantiateViewController(withIdentifier: "DetailsViewController")
as? DetailsViewController {
detailsVc.annotation = annotationView.annotation
if let annotation = annotationView.annotation as? Place {
detailsVc.place = annotation
}
self.navigationController?.pushViewController(detailsVc, animated: true)
}
}
}
}
Just looking over your code quickly:
"\(nearMeAnnotation.nearMeRequest.phone)"
All the other ones have a forced unwrap, this one doesn't. Most probably the value is nil and since you ask for a string representation of a wrapped var that might really be nil sometimes.
I think you should use a default value everywhere instead of a forced unwrap, like:
"\(nearMeAnnotation.nearMeRequest.phone ?? "")"
but also:
"\(nearMeAnnotation.nearMeRequest.street ?? "") \(nearMeAnnotation.nearMeRequest.state ?? "") \(nearMeAnnotation.nearMeRequest.state ?? "") \(nearMeAnnotation.nearMeRequest.zip ?? "")"
With forced unwraps your application will crash if a certain value is not set. This could be handled more elegantly if they're really required, for example already in the constructor of your object. There's the root cause of the optionals you're seeing. In this case NearMeAnnotation.

Add two coordinates in a button function to launch mapKit and start navigation between two points (Swift)

I'm using this class
import UIKit
import CoreLocation
import GoogleMaps
import GooglePlaces
import SwiftyJSON
import Alamofire
import MapKit
class FinalClass: UIViewController {
#IBOutlet weak var containerView: UIView!
#IBOutlet weak var bottomInfoView: UIView!
#IBOutlet weak var descriptionLabel: UILabel!
#IBOutlet weak var distanceLabel: UILabel!
var userLocation:CLLocationCoordinate2D?
var places:[QPlace] = []
var index:Int = -1
var locationStart = CLLocation()
var locationEnd = CLLocation()
var mapView:GMSMapView!
var marker:GMSMarker?
override func loadView() {
super.loadView()
}
override func viewDidLoad() {
super.viewDidLoad()
guard index >= 0, places.count > 0 else {
return
}
let place = places[index]
let lat = place.location?.latitude ?? 1.310844
let lng = place.location?.longitude ?? 103.866048
// Google map view
let camera = GMSCameraPosition.camera(withLatitude: lat, longitude: lng, zoom: 12.5)
mapView = GMSMapView.map(withFrame: self.view.bounds, camera: camera)
mapView.autoresizingMask = [.flexibleHeight, .flexibleWidth, .flexibleTopMargin, .flexibleBottomMargin, .flexibleLeftMargin, .flexibleRightMargin]
self.containerView.addSubview(mapView)
// Add gesture
addSwipeGesture()
didSelect(place: place)
if userLocation != nil {
addMarkerAtCurrentLocation(userLocation!)
}
}
func addSwipeGesture() {
let directions: [UISwipeGestureRecognizerDirection] = [.right, .left]
for direction in directions {
let gesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(sender:)))
gesture.direction = direction
self.bottomInfoView.addGestureRecognizer(gesture)
}
}
func addMarkerAtCurrentLocation(_ userLocation: CLLocationCoordinate2D) {
let marker = GMSMarker()
marker.position = userLocation
marker.title = "Your location"
marker.map = mapView
}
func didSelect(place:QPlace) {
guard let coordinates = place.location else {
return
}
// clear current marker
marker?.map = nil
// add marker
marker = GMSMarker()
marker?.position = coordinates
marker?.title = place.name
marker?.map = mapView
mapView.selectedMarker = marker
moveToMarker(marker!)
// update bottom info panel view
let desc = place.getDescription()
descriptionLabel.text = desc.characters.count > 0 ? desc : "-"
distanceLabel.text = "-"
// update distance
if userLocation != nil {
let dist = distance(from: userLocation!, to: coordinates)
distanceLabel.text = String.init(format: "Distance %.2f meters", dist)
self.drawPath(startLocation: userLocation!, endLocation: coordinates)
}
title = place.name
}
func moveToMarker(_ marker: GMSMarker) {
let camera = GMSCameraPosition.camera(withLatitude: marker.position.latitude,
longitude: marker.position.longitude,
zoom: 12.5)
self.mapView.animate(to: camera)
}
// distance between two coordinates
func distance(from: CLLocationCoordinate2D, to: CLLocationCoordinate2D) -> CLLocationDistance {
let from = CLLocation(latitude: from.latitude, longitude: from.longitude)
let to = CLLocation(latitude: to.latitude, longitude: to.longitude)
return from.distance(from: to)
}
func handleSwipe(sender: UISwipeGestureRecognizer) {
guard index >= 0, places.count > 0 else {
return
}
if sender.direction == .left {
if index < places.count - 2 {
index += 1
didSelect(place: places[index])
}
} else if sender.direction == .right {
if index > 1 {
index -= 1
didSelect(place: places[index])
}
}
}
func drawPath(startLocation: CLLocationCoordinate2D, endLocation: CLLocationCoordinate2D) {
let from = CLLocation(latitude: startLocation.latitude, longitude: startLocation.longitude)
let to = CLLocation(latitude: endLocation.latitude, longitude: endLocation.longitude)
let origin = "\(from.coordinate.latitude),\(from.coordinate.longitude)"
let destination = "\(to.coordinate.latitude),\(to.coordinate.longitude)"
let url = "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin)&destination=\(destination)&mode=driving"
Alamofire.request(url).responseJSON { response in
print(response.request as Any) // original URL request
print(response.response as Any) // HTTP URL response
print(response.data as Any) // server data
print(response.result as Any) // result of response serialization
let json = JSON(data: response.data!)
let routes = json["routes"].arrayValue
// print route using Polyline
for route in routes
{
let routeOverviewPolyline = route["overview_polyline"].dictionary
let points = routeOverviewPolyline?["points"]?.stringValue
let path = GMSPath.init(fromEncodedPath: points!)
let polyline = GMSPolyline.init(path: path)
polyline.strokeWidth = 4
polyline.strokeColor = UIColor.black
polyline.map = self.mapView
}
}
}
#IBAction func navigationStart(_ sender: Any) {
}
with a google maps to add place markers, draw the direction on the map and show the distance between two points, now i would like to launch the navigator between startLocation: userLocation! and endLocation: coordinates but with some research i saw that i can not launch the navigator in the same view, i need to open the maps application, so i decided to add the MapKit and a button
#IBAction func navigationStart(_ sender: Any) {
}
so how can i do that by pressing the button the map application opens with direction from userLocation to coordinates ? I already looked to similar question but is a little different to my problem, because i already have the points but in different format.
Your question is a little confusing but if you want to open the maps app and show direction from a user's current location to another point on the map then you don't need to pass the user's location, just the destination:
Swift 4:
let coordinate = CLLocationCoordinate2DMake(51.5007, -0.1246)
let placeMark = MKPlacemark(coordinate: coordinate)
let mapItem = MKMapItem(placemark: placeMark)
mapItem.name = "Big Ben"
mapItem.openInMaps(launchOptions: [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving])
Objective C:
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(51.5007, -0.1246);
MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate: coordinate];
MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
[mapItem setName:#"Big Ben"];
[mapItem openInMapsWithLaunchOptions:#{MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving}];

How to cluster custom icons markers in GoogleMaps for iOS

I'm developing an app on which I want to show a lot of event on the map. The user can click on an event and see a lot of information about it.
I customized the marker icon of each event with a custom image and now I want to cluster each custom markers.
I'm able to cluster the default icon of GoogleMaps API but if I want to cluster my own marker icon I'm not able to do it.
Here's my current code :
var mapView: GMSMapView!
var clusterManager: GMUClusterManager!
let isClustering: Bool = true
let isCustom: Bool = true
override func viewDidLoad() {
super.viewDidLoad()
mapView = GMSMapView(frame: view.frame)
mapView.camera = GMSCameraPosition.camera(withLatitude: 13.756331, longitude: 100.501765, zoom: 12.0)
mapView.mapType = .normal
mapView.delegate = self
view.addSubview(mapView)
if isClustering {
var iconGenerator: GMUDefaultClusterIconGenerator!
if isCustom { // Here's my image if the event are clustered
var images: [UIImage] = [UIImage(named: "m1.png")!, UIImage(named: "m2.png")!, UIImage(named: "m3.png")!, UIImage(named: "m4.png")!, UIImage(named: "m5.png")!]
iconGenerator = GMUDefaultClusterIconGenerator(buckets: [5, 10, 15, 20, 25], backgroundImages: images)
} else {
iconGenerator = GMUDefaultClusterIconGenerator()
}
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
clusterManager.cluster()
clusterManager.setDelegate(self, mapDelegate: self)
} else {
}
// Here's my personal marker icon (for one location)
let firstLocation = CLLocationCoordinate2DMake(48.898902, 2.282664)
let marker = GMSMarker(position: firstLocation)
marker.icon = UIImage(named: "pointeurx1") //Apply custom marker
marker.map = mapView
let secondLocation = CLLocationCoordinate2DMake(48.924572, 2.360207)
let secondMarker = GMSMarker(position: secondLocation)
secondMarker.icon = UIImage(named: "pointeurx1")
secondMarker.map = mapView
let threeLocation = CLLocationCoordinate2DMake(48.841619, 2.253113)
let threeMarker = GMSMarker(position: threeLocation)
threeMarker.icon = UIImage(named: "pointeurx1")
threeMarker.map = mapView
let fourLocation = CLLocationCoordinate2DMake(48.858575, 2.294556)
let fourMarker = GMSMarker(position: fourLocation)
fourMarker.icon = UIImage(named: "pointeurx1")
fourMarker.map = mapView
let fiveLocation = CLLocationCoordinate2DMake(48.873819, 2.295200)
let fiveMarker = GMSMarker(position: fiveLocation)
fiveMarker.icon = UIImage(named: "pointeurx1")
fiveMarker.map = mapView
}
/// Point of Interest Item which implements the GMUClusterItem protocol.
class POIItem: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
var name: String!
init(position: CLLocationCoordinate2D, name: String) {
self.position = position
self.name = name
}
}
func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) {
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position, zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
}
}
How can I do it?
Look at these screenshots of my app then maybe you could understand better my issue.
First one there are the red default markers icons of the Google Maps, you can see in blue the cluster icon I was added in my project. Then you understand I added some locations on the viewDidLoad(), then the red markers are that. You can also see two others differents markers, the google one is orange and another one is my personal marker icon that I want to use for each marker icon of a location. But you can also see the issue, the issue is the blue cluster icon don't add the markers icons I added on the map (it shows 4 inside the blue cluster icon, it's the 4 icons around it but when the blue cluster icon appears the markers icons around it don't disappear.
Second image, if I make a zoom, the blue cluster icon disappears but you can also see another issue, the locations I was added have another red default markers icons of the Google Maps appears above them (you can see it at less because of my personal orange marker icon
You are actually clustering first then adding markers thats why this is happening.
What you should actually do is
class MarkerModel: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
var name: String
init(position: CLLocationCoordinate2D, name: String) {
self.position = position
self.name = name
}
}
override func viewDidLoad() {
super.viewDidLoad()
mapView = GMSMapView(frame: view.frame)
mapView.camera = GMSCameraPosition.camera(withLatitude: 13.756331, longitude: 100.501765, zoom: 12.0)
mapView.mapType = .normal
mapView.delegate = self
view.addSubview(mapView)
if isClustering {
var iconGenerator: GMUDefaultClusterIconGenerator!
if isCustom { // Here's my image if the event are clustered
var images: [UIImage] = [UIImage(named: "m1.png")!, UIImage(named: "m2.png")!, UIImage(named: "m3.png")!, UIImage(named: "m4.png")!, UIImage(named: "m5.png")!]
iconGenerator = GMUDefaultClusterIconGenerator(buckets: [5, 10, 15, 20, 25], backgroundImages: images)
} else {
iconGenerator = GMUDefaultClusterIconGenerator()
}
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
} else {
}
}
func addMarkers(cameraLatitude : Float, cameraLongitude : Float) {
let extent = 0.01
for index in 1...clusterItemCount {
let lat = cameraLatitude + extent * randomScale()
let lng = cameraLongitude + extent * randomScale()
let name = "Item \(index)"
let position = CLLocationCoordinate2DMake(lat, lng)
let item = MarkerModel(position: position, name: name)
item.icon = #imageLiteral(resourceName: "marker")
clusterManager.add(item)
}
clusterManager.cluster()
clusterManager.setDelegate(self, mapDelegate: self)
}
func randomScale() -> Double {
return Double(arc4random()) / Double(UINT32_MAX) * 2.0 - 1.0
}
func renderer(_ renderer: GMUClusterRenderer, markerFor object: Any) -> GMSMarker? {
let marker = GMSMarker()
if let model = object as? MarkerModel {
// set image view for gmsmarker
}
return marker
}
func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) -> Bool {
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position, zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
return false
}

Offline map tiles have stopped displaying

I have downloaded tiles for a small town in zooms 15 to 18. They were displaying as expected but now don't.
I was using xcode 7 with swift 2 on Yosemite. I'm now with xcode 8.1 with swift 3 on sierra. I'm pretty sure the problem started before the upgrade, but have no time machine to go back. I upgraded because I wanted to test the app on my IPad 2, got the message that xcode wasn't compatible with the ios on the ipad, then got a message that xcode 8.1 cant be loaded to Yosemite hence upgrade to sierra.
The code is below. And the console output bellow that. No error given
//
// MapViewController.swift
// button scale
//
// Created by Colin McGarry on 24/03/16.
// Copyright © 2016 Colin McGarry. All rights reserved.
//
import UIKit
import MapKit
import CoreLocation
class MapViewController: UIViewController /*, MKMapViewDelegate*/{
var guideData: GuideData?
var dataM:[GuideData] = [GuideData]()
var placeNumb = 0
var modifyingMap = true
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
let IS_RETINA = (UIScreen.mainScreen().respondsToSelector(#selector(UIScreen.displayLinkWithTarget(_:selector:))) && (UIScreen.mainScreen().scale >= 2.0))
// from shankar map
if IS_RETINA {
print("Is Retina")
} else {
print("not retina")
}
zoomToRegion()
let annotations = getMapAnnotations()
// Add mappoints to Map
mapView.addAnnotations(annotations)
// end shankar map
//Get the URL template to the map tiles
let baseURL = NSBundle.mainBundle().bundleURL.absoluteString
let urlTemplate = baseURL!.stringByAppendingString("osmm/{z}/{x}/{y}.png/")
//let urlTemplate = baseURL.stringByAppendingString("two/{z}/{x}/{y}#2x.png/")
//let urlTemplate = "http://tile.openstreetmap.org/{z}/{x}/{y}.png"
print(urlTemplate)
let carte_indice = MKTileOverlay(URLTemplate:urlTemplate)
carte_indice.geometryFlipped = false
carte_indice.canReplaceMapContent = true
//carte_indice.tileSize = CGSize(width: 512, height: 512)
carte_indice.maximumZ = 16
carte_indice.minimumZ = 18
self.mapView.addOverlay(carte_indice)
}
/// from shankar map
func zoomToRegion() {
let location = CLLocationCoordinate2D(latitude: 49.275, longitude: -0.7028)
let region = MKCoordinateRegionMakeWithDistance(location, 500.0, 500.0)
mapView.setRegion(region, animated: true)
}
//MARK:- Annotations
func getMapAnnotations() -> [Stands] {
var annotations:Array = [Stands]()
//load plist file
var stands: NSArray?
if let path = NSBundle.mainBundle().pathForResource("stands", ofType: "plist") {
stands = NSArray(contentsOfFile: path)
}
//iterate and create annotations
if let items = stands {
for item in items {
let lat = item.valueForKey("lat") as! Double
let long = item.valueForKey("long")as! Double
let annotation = Stands(latitude: lat, longitude: long)
let tit = item.valueForKey("title") as! String
let numb = item.valueForKey("no") as! Int
annotation.title = "\(numb) \(tit)"
annotation.no = numb
// new added
// annotation.enabled = true
// annotation.canShowCallOut = true
// end added
annotations.append(annotation)
}
}
return annotations
}
// end From Shankar map
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
// 1
let identifier = "Stand"
// 2
if annotation.isKindOfClass(Stands.self) {
// 3
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
if annotationView == nil {
//4
annotationView = MKPinAnnotationView(annotation:annotation, reuseIdentifier:identifier)
annotationView!.canShowCallout = true
// 5
let btn = UIButton(type: .DetailDisclosure)
annotationView!.rightCalloutAccessoryView = btn
} else {
// 6
annotationView!.annotation = annotation
}
return annotationView
}
// 7
return nil
}
func mapView( mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
let standM = view.annotation as! Stands
let placeName = standM.title
let placeInfo = standM.title
placeNumb = standM.no!
/*
let ac = UIAlertController(title: placeName, message: placeInfo, preferredStyle: .Alert)
ac.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
presentViewController(ac, animated: true, completion: nil) */
performSegueWithIdentifier("MapToText", sender: self)
}
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
// enforce maximum zoom level
let place = mapView.visibleMapRect
if( place.origin.x < 133686298
|| place.origin.x > 133697376
|| place.origin.y < 91864219
|| place.origin.y > 91874305
)
{
zoomToRegion()
}
print("alt \(mapView.camera.altitude)")
let maxAlt = 3000.00
if (mapView.camera.altitude > maxAlt && self.modifyingMap)
{
self.modifyingMap = false
// prevents strange infinite loop case
self.mapView.zoomEnabled = false
/* self.mapView.scrollEnabled = false
self.mapView.userInteractionEnabled = false
*/
print("place \(place)")
self.mapView.camera.altitude = maxAlt
self.modifyingMap = true
print("alt>\(maxAlt)")
} else {
self.mapView.zoomEnabled = true
print("place2 \(place)")
print("x \(place.origin.x)")
print(" alt less than \(maxAlt)")
print(mapView.camera.altitude)
}
}
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer!
{
print("call overlay")
if overlay is MKTileOverlay
{
print("is MKTileoverlay")
let renderer = MKTileOverlayRenderer(overlay:overlay)
renderer.alpha = 0.8
return renderer
}
return nil
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "MapToText" {
let webviewController:webViewController = segue.destinationViewController as! webViewController
webviewController.index = placeNumb - 1
// webviewController.standTitle = sender as! MKAnnotationView
webviewController.inde = placeNumb - 1
print("prepSeg \(webviewController.inde)")
}
}
}
console output
/Users/colinmcgarry/Library/Developer/CoreSimulator/Devices/4F5E35FC-45F9-4A16-8A0A-1AFD51616CCA/data/Containers/Bundle/Application/046B53E5-D3CD-4E7A-926D-7D77E069AAA0/button scale.app/stands.plist
Optional("/Users/colinmcgarry/Library/Developer/CoreSimulator/Devices/4F5E35FC-45F9-4A16-8A0A-1AFD51616CCA/data/Containers/Bundle/Application/046B53E5-D3CD-4E7A-926D-7D77E069AAA0/button scale.app/2page.html")
Is Retina
alt 1100.94691259802
place2 MKMapRect(origin: __C.MKMapPoint(x: 133691120.58898854, y: 91870217.34526816), size: __C.MKMapSize(width: 5123.4971518665552, height: 6063.3100336045027))
x 133691120.588989
alt less than 3000.0
1100.94691259802
file:///Users/colinmcgarry/Library/Developer/CoreSimulator/Devices/4F5E35FC-45F9-4A16-8A0A-1AFD51616CCA/data/Containers/Bundle/Application/046B53E5-D3CD-4E7A-926D-7D77E069AAA0/button%20scale.app/osmm/{z}/{x}/{y}.png/
call overlay
is MKTileoverlay
I found the problem. But it's not in the code I included.
I had put min and max zoom. When I changed the values I interchanged min and max
overlay.maximumZ = 15
overlay.minimumZ = 17
These conditions are mutually contradicting so overlay didn't show.
if I'd written min above max I might have noticed before.

Remove annotations from map and replace with updated annotations (qTree)

I am currently working on adding cluster annotations to my map view. Everything is working fine with the exception of a couple of things. First of all, I need to refresh the annotations on the map when the user leaves and returns to the view. At the moment this sort of works... The problem is that rather than removing the annotations and adding the new ones, the new ones are just being added in addition of the old ones so there are multiplying each time.
The next issue is that each annotation displays the distance the user is from that annotation, however the annotations are set up before the user's location is found. I assume I will just have to remove and replace the annotations once the location is found, but then I run into the issue of the annotations duplicating again.
This is my code:
override func viewDidAppear(animated: Bool) {
println(rideArray.count)
setUpMapView()
}
func setUpMapView() {
rideArray = ((DataManager.sharedInstance.rideArray) as NSArray) as! [Ride]
myLocation = mapView.userLocation.coordinate as CLLocationCoordinate2D
zoomRegion = MKCoordinateRegionMakeWithDistance(CLLocationCoordinate2D(latitude: parkPassed.latitude!, longitude: parkPassed.longitude!), 1000, 1000)
mapView.setRegion(zoomRegion, animated: true)
mapView.delegate = self
for ride in rideArray {
println("Location: \(mapView.userLocation.coordinate.latitude)")
var subtitle = ""
if mapView.userLocation.location == nil {
subtitle = "Distance unavailable"
} else {
let userLocation = CLLocation(latitude: mapView.userLocation.coordinate.latitude, longitude: mapView.userLocation.coordinate.longitude)
let annotationLocation = CLLocation(latitude: ride.latitude!, longitude: ride.longitude!)
var distance = Int(CLLocationDistance(annotationLocation.distanceFromLocation(userLocation)))
if distance > 1000 {
distance = distance / 1000
subtitle = "\(distance) kilometers"
} else {
subtitle = "\(distance) meters"
}
}
let annotation = RideAnnotation(coordinate: CLLocationCoordinate2DMake(ride.latitude!, ride.longitude!), title: ride.name!, subtitle: subtitle)
self.qTree.insertObject(annotation)
}
var leftSwipe = UISwipeGestureRecognizer(target: self, action: Selector("handleSwipes:"))
var rightSwipe = UISwipeGestureRecognizer(target: self, action: Selector("handleSwipes:"))
leftSwipe.direction = .Left
rightSwipe.direction = .Right
view.addGestureRecognizer(leftSwipe)
view.addGestureRecognizer(rightSwipe)
}
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if annotation.isKindOfClass(QCluster.classForCoder()) {
let PinIdentifier = "PinIdentifier"
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(ClusterAnnotationView.reuseId()) as? ClusterAnnotationView
if annotationView == nil {
annotationView = ClusterAnnotationView(cluster: annotation)
}
annotationView!.cluster = annotation
return annotationView
} else if annotation.isKindOfClass(RideAnnotation.classForCoder()) {
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier("RideAnnotation")
if pinView == nil {
pinView = MKAnnotationView(annotation: annotation, reuseIdentifier: "RideAnnotation")
pinView?.canShowCallout = true
pinView?.rightCalloutAccessoryView = UIButton.buttonWithType(UIButtonType.DetailDisclosure) as! UIButton
pinView?.rightCalloutAccessoryView.tintColor = UIColorFromRGB(0x424242)
let rideTimeView = UIView()
rideTimeView.frame = CGRectMake(5, 5, 50, 50)
rideTimeView.layer.cornerRadius = 25
let waitTimeLabel = UILabel()
waitTimeLabel.frame = CGRectMake(0, 0, 50, 50)
if DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime! == "Closed" {
waitTimeLabel.text = "\(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!)"
rideTimeView.backgroundColor = getColorFromNumber(80)
waitTimeLabel.font = UIFont(name: "Avenir", size: 15)
} else {
waitTimeLabel.text = "\(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!)m"
rideTimeView.backgroundColor = getColorFromNumber(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!.toInt()!)
waitTimeLabel.font = UIFont(name: "Avenir", size: 20)
}
waitTimeLabel.textAlignment = NSTextAlignment.Center
waitTimeLabel.textColor = UIColor.whiteColor()
waitTimeLabel.adjustsFontSizeToFitWidth = true
waitTimeLabel.numberOfLines = 1
rideTimeView.addSubview(waitTimeLabel)
pinView.leftCalloutAccessoryView = rideTimeView
pinView?.image = UIImage(named: "rideMapAnnotation")
} else {
pinView?.annotation = annotation
let rideTimeView = UIView()
rideTimeView.frame = CGRectMake(5, 5, 50, 50)
rideTimeView.layer.cornerRadius = 25
let waitTimeLabel = UILabel()
waitTimeLabel.frame = CGRectMake(0, 0, 50, 50)
if DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime! == "Closed" {
waitTimeLabel.text = "\(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!)"
rideTimeView.backgroundColor = getColorFromNumber(80)
waitTimeLabel.font = UIFont(name: "Avenir", size: 15)
} else {
waitTimeLabel.text = "\(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!)m"
rideTimeView.backgroundColor = getColorFromNumber(DataManager.sharedInstance.getRideByName(annotation.title!)!.waitTime!.toInt()!)
waitTimeLabel.font = UIFont(name: "Avenir", size: 20)
}
waitTimeLabel.textAlignment = NSTextAlignment.Center
waitTimeLabel.textColor = UIColor.whiteColor()
waitTimeLabel.adjustsFontSizeToFitWidth = true
waitTimeLabel.numberOfLines = 1
rideTimeView.addSubview(waitTimeLabel)
pinView.leftCalloutAccessoryView = rideTimeView
}
return pinView
}
return nil
}
func reloadAnnotations(){
if self.isViewLoaded() == false {
return
}
self.cacheArray.removeAll(keepCapacity: false)
let mapRegion = self.mapView.region
let minNonClusteredSpan = min(mapRegion.span.latitudeDelta, mapRegion.span.longitudeDelta) / 5
let objects = self.qTree.getObjectsInRegion(mapRegion, minNonClusteredSpan: minNonClusteredSpan) as NSArray
for object in objects {
if object.isKindOfClass(QCluster){
let c = object as? QCluster
let neighbours = self.qTree.neighboursForLocation((c?.coordinate)!, limitCount: NSInteger((c?.objectsCount)!)) as NSArray
for neighbour in neighbours {
let tmp = self.rideArray.filter({
return $0.name == (neighbour.title)!!
})
if find(self.cacheArray, tmp[0]) == nil {
self.cacheArray.insert(tmp[0], atIndex: self.cacheArray.count)
}
}
} else {
let tmp = self.rideArray.filter({
return $0.name == (object.title)!!
})
if find(self.cacheArray, tmp[0]) == nil {
self.cacheArray.insert(tmp[0], atIndex: self.cacheArray.count)
}
}
}
let annotationsToRemove = (self.mapView.annotations as NSArray).mutableCopy() as! NSMutableArray
annotationsToRemove.removeObject(self.mapView.userLocation)
annotationsToRemove.removeObjectsInArray(objects as [AnyObject])
self.mapView.removeAnnotations(annotationsToRemove as [AnyObject])
let annotationsToAdd = objects.mutableCopy() as! NSMutableArray
annotationsToAdd.removeObjectsInArray(self.mapView.annotations)
self.mapView.addAnnotations(annotationsToAdd as [AnyObject])
}
func mapView(mapView: MKMapView!, regionDidChangeAnimated animated: Bool) {
viewChanged = true
self.reloadAnnotations()
}
I apologise for the extensive amount of code, but it all had to be included.
Anyone have any suggestions as to how I can remove the annotations and re-add them once the view appears?
Thanks!
EDIT:
The second issue is now resolved. Here is a bit more detail in regards to the situation and issue:
Basically each annotation represents a ride in a theme park and displays the ride name, current wait time and distance to that ride. At the moment since I am calling setUpMapView() when the view appears, all of the ride annotations are being added to the qTree each time, but are not being removed. This is the issue that I am trying to resolve, but I can't find a way to remove them from the qTree.
MKMapView has the method removeAnnotation:, which will remove a single annotation, and removeAnnotations: which will remove an array of annotations. It also has an annotations property which lets you the current array of annotations. You should be able to select and remove the annotations that you want to remove and then add new ones to replace them.
I think you could try finding user location like this
let locationManager: CLLocationManager = CLLocationManager()
(write this as a class property, not inside any viewDidLoad!)
then u instantly have locationManager coordinates.
self.mapView.removeAnnotations(annotationsToRemove as [AnyObject])
could you print the "annotationsToRemove as [AnyObject]" and tell us what is the content ?
actually all this code is suspicious:
let annotationsToRemove = (self.mapView.annotations as NSArray).mutableCopy() as! NSMutableArray
annotationsToRemove.removeObject(self.mapView.userLocation)
annotationsToRemove.removeObjectsInArray(objects as [AnyObject])
self.mapView.removeAnnotations(annotationsToRemove as [AnyObject])
you define a constant, then edit it with removeObjects and then pass it to removeAnnotations ? strange man, please get as many prints on these lines as you can and tell what they are so we can help

Resources