I'm making an app that measures speed and distance in real time. so before did that app starts updating location when button tapped. but this method takes time after button tapped. if i will put locationManager.startUpdatingLocation in viewDidLoad, speed and distance starts measure immediately without tapping start button. How can I do like speed and distance starts measure immediately ONLY WHEN when start button tapped? here's my code.
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
#IBAction func startStopButtonDidTouch(_ sender: UIButton) {
if isStarted { //When tapped STOP
locationManager.stopUpdatingLocation()
startStopButton.setTitle("START", for: .normal)
} else { //When tapped START
startStopButton.setTitle("STOP", for: .normal)
}
}
func locationManager (_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
speedLabel.text = "\(Int(location.speed * 3.6))"
if (startLocation == nil) {
startLocation = location;
}
let endLocation = location;
let distance = startLocation.distance(from: endLocation) / 1000 //m->km
distanceLabel.text = "\(String(format: "%.1f", distance)) km"
}
I'm not sure this is the best solution, but you might use an extra variable to make sure that when you are getting the location on app start, the speed and distance calculations should not be done. Something like this:
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
isToPerformCalculations = false
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
#IBAction func startStopButtonDidTouch(_ sender: UIButton) {
if isStarted { //When tapped STOP
// locationManager.stopUpdatingLocation()
startStopButton.setTitle("START", for: .normal)
} else { //When tapped START
isToPerformCalculations = true
startStopButton.setTitle("STOP", for: .normal)
}
}
func locationManager (_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
locationManager.stopUpdatingLocation()
if isToPerformCalculations {
if (startLocation == nil) {
startLocation = location;
}
let endLocation = location;
speedLabel.text = "\(Int(location.speed * 3.6))"
let distance = startLocation.distance(from: endLocation) / 1000 //m->km
distanceLabel.text = "\(String(format: "%.1f", distance)) km"
}
}
I also changed where you stopUpdatingLocation, see if this works for you.
Related
Here is the problem. In my view controller, I have placed a segmented control on the top.
On controller load it displays the map, circle and the marker. Now, when I click the segmented control to change the map type it goes into the code block on select but never updates the Map type.
Please see the code here.
class MapController: UIViewController, CLLocationManagerDelegate , GMSMapViewDelegate {
var locationManager:CLLocationManager!
let marker = GMSMarker()
var roundCircle: GMSCircle!
var mapView = GMSMapView()
private var didPerformGeocode = false
enum maptype:NSInteger
{
case standardmap = 0
case satellitemap = 1
}
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func segmentedControlAction(_ sender: UISegmentedControl)
{
switch sender.selectedSegmentIndex {
case maptype.standardmap.rawValue:
mapView.mapType = .normal
**no change in the map type**
case maptype.satellitemap.rawValue:
mapView.mapType = .satellite
**no change in the map type**
default:
break
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
determineMyCurrentLocation()
}
func determineMyCurrentLocation() {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0] as CLLocation
guard !didPerformGeocode else { return }
didPerformGeocode = true
locationManager.stopUpdatingLocation()
let camera = GMSCameraPosition.camera(withLatitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude, zoom: 13.0)
let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
mapView.delegate = self
self.view = mapView
let markerImage = UIImage(named: "mapMarker")!.withRenderingMode(.alwaysTemplate)
let markerView = UIImageView(image: markerImage)
markerView.tintColor = UIColor.red
marker.iconView = markerView
marker.position = CLLocationCoordinate2D(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude)
marker.map = mapView
roundCircle = GMSCircle(position: camera.target, radius: 1500) //0.96 Miles
roundCircle.fillColor = UIColor.red.withAlphaComponent(0.3)
roundCircle.strokeColor = nil
roundCircle.map = mapView
}
}
Honestly, I don't use Google Maps
However, to change MapTypes in Apple Maps I use
#IBAction func Whatever(_ sender: UISegmentedControl) {
mapView.mapType = MKMapType.init(rawValue: UInt(sender.selectedSegmentIndex)) ?? .normal
}
Maybe it will work
In Apple’s iOS Reminders app, you can set a location area at which you would be reminded of your reminder. When you set the location area, you are allowed to draw a circle that covers the area of the location you want to be reminded at. How would I duplicate this feature. I particularly would like to know how to allow the user to resize the circle on a map view by dragging his finger on the edge of the circle. I have already figured out how to create the circle.
Here is my code so far:
import UIKit
import MapKit
class ViewController: UIViewController {
// MARK: - Outlets
#IBOutlet weak var mapView: MKMapView!
// MARK: - Variables and Constants
let regionRadius: CLLocationDistance = 1000
let circularRegionRadius: CLLocationDistance = 500
let locationManager = CLLocationManager()
// MARK: - View
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
centerMapOnLocation(location: locationManager.location!)
}
// MARK: - Actions
#IBAction func addRegion(_ sender: UILongPressGestureRecognizer) {
print("addRegion(_:)")
let longPress = sender
let touchLocation = longPress.location(in: mapView)
let coordinates = mapView.convert(touchLocation, toCoordinateFrom: mapView)
let region = CLCircularRegion(center: coordinates, radius: circularRegionRadius, identifier: "geofence")
mapView.removeOverlays(mapView.overlays)
locationManager.startMonitoring(for: region)
let circle = MKCircle(center: coordinates, radius: region.radius)
mapView.add(circle)
}
// MARK: - Helper Functions
func centerMapOnLocation(location: CLLocation) {
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,
regionRadius, regionRadius)
mapView.setRegion(coordinateRegion, animated: true)
}
// MARK: - Memory Warning
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
print("mapView(_:rendererFor:_:)")
guard let circelOverLay = overlay as? MKCircle else {return MKOverlayRenderer()}
let circleRenderer = MKCircleRenderer(circle: circelOverLay)
circleRenderer.strokeColor = .blue
circleRenderer.fillColor = .blue
circleRenderer.alpha = 0.2
return circleRenderer
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("locationManager(_:didUpdateLocations:)")
locationManager.stopUpdatingLocation()
mapView.showsUserLocation = true
}
}
I have a tab bar, one is used to display the map. When I switch to this tab bar, it will request permission and get the current location automatically.
I finished the code, set the simulated location to Apple, it will show a blue dot in the center of the screen, but when I chose the custom location, specify a location, the blue dot is not displayed in the center of the map.
I need to drag the map, and will find the blue dot near the center location about 1~2km. I tested on the real device and have the same problem.
use the method to show the current location auto, but not show the blue dot.
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager!.delegate = self
if CLLocationManager.authorizationStatus() == .authorizedWhenInUse{
locationManager!.startUpdatingLocation()
locationManager!.desiredAccuracy = kCLLocationAccuracyBestForNavigation
}else{
locationManager!.requestWhenInUseAuthorization()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let newLocation = locations.last!
let coordinateRegion = MKCoordinateRegionMakeWithDistance(newLocation.coordinate, 100, 100)
mapView.setRegion(mapView.regionThatFits(coordinateRegion), animated: true)
location = newLocation
locationManager?.stopUpdatingLocation()
locationManager = nil
}
I added a button - when it not show the blue dot, clicking the button will set the blue dot in the center of the map. The two methods have the same function, but they produce different views. Why?
#IBAction func showUser() {
let region = MKCoordinateRegionMakeWithDistance(mapView.userLocation.coordinate, 100, 100)
mapView.setRegion(mapView.regionThatFits(region), animated: true)
}
Screenshot
I just use this coding to update to location with center of mapview
Please add this keys into info.plist file
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<key>NSLocationAlwaysUsageDescription</key>
<string></string>
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
locationManager.delegate = self
map.delegate = self
map.showsUserLocation = true
if CLLocationManager.authorizationStatus() == .authorizedWhenInUse{
locationManager.startUpdatingLocation()
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
}else{
locationManager.requestWhenInUseAuthorization()
}
}
func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
mapView .setCenter(userLocation.coordinate, animated: true)
}
You can download demo from HERE
I have a issue with my swipe gesture on a MapView, to be more accurate I have issues with the first swipe gesture on a MapView because it does not does a function & moves the screen at the same time.
The code recognize my location, put an annotation on me, and runs a 5 sec timer that allows the app to add new annotations only every 5 secs. The MapView follows my path as usually. All good so far. Check the pic of my simulator at the end.
Then it occurred to me that it would be a good idea to allow the user to scape from that region and explore the rest of the map. The user can do that only swiping in any direction. And when the user is at any other location he can press the "getBack" button to center the map on his current location. All works good too.
The issue is that when the gesture Swipe is detected , that first swipe does not move the screen. It is only the second swift which does it.
I think that may be the first swipe only stops the map from following my path, and then the second one is the one that moves the screen.
Is there a way for the first swipe to stop following my path and move the map at the same time ?
I hope I made myself clear :(
The Code of the ViewController:
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet var map: MKMapView!
var locationManager = CLLocationManager()
let latDelta:CLLocationDegrees = 0.05
let longDelta:CLLocationDegrees = 0.05
var reStartCountClock = true
var getBackInPlace = true
var timer = NSTimer()
#IBAction func getBack(sender: AnyObject) {
getBackInPlace = true
}
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
let uilpgr = UILongPressGestureRecognizer(target: self, action: "actionWhenPressed:")
uilpgr.minimumPressDuration = 1
map.addGestureRecognizer(uilpgr)
let uilpgrD = UISwipeGestureRecognizer(target: self, action:Selector("handleSwipe:"))
uilpgrD.direction = .Down
let uilpgrU = UISwipeGestureRecognizer(target: self, action:Selector("handleSwipe:"))
uilpgrU.direction = .Up
let uilpgrR = UISwipeGestureRecognizer(target: self, action:Selector("handleSwipe:"))
uilpgrR.direction = .Right
let uilpgrL = UISwipeGestureRecognizer(target: self, action:Selector("handleSwipe:"))
uilpgrL.direction = .Left
map.addGestureRecognizer(uilpgrD)
map.addGestureRecognizer(uilpgrR)
map.addGestureRecognizer(uilpgrL)
map.addGestureRecognizer(uilpgrU)
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locationOnline:CLLocation = locations[0]
let spanOnline:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, longDelta)
let centerOnline:CLLocationCoordinate2D = CLLocationCoordinate2DMake(locationOnline.coordinate.latitude, locationOnline.coordinate.longitude)
let regionOnline:MKCoordinateRegion = MKCoordinateRegionMake(centerOnline, spanOnline)
if getBackInPlace == true {
self.map.setRegion(regionOnline, animated: true)
}
if reStartCountClock == true {
timer = NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: "Anota", userInfo: nil, repeats: true)
reStartCountClock = false
let annotationOnline = MKPointAnnotation()
annotationOnline.title = "Updated Location"
annotationOnline.subtitle = "Pin Pam Pum"
annotationOnline.coordinate = centerOnline
self.map.addAnnotation(annotationOnline)
}
}
func handleSwipe (gestureRecognizer: UIGestureRecognizer)
{
getBackInPlace = false
print("Entra en el gesto")
}
func Anota (){
reStartCountClock = true
timer.invalidate()
}
func actionWhenPressed (gestureRecognizer: UIGestureRecognizer) {
if gestureRecognizer.state == UIGestureRecognizerState.Began
{
getBackInPlace = false
print("Gesture Recognized")
let touch = gestureRecognizer.locationInView(self.map)
let touchCoordinate:CLLocationCoordinate2D = map.convertPoint(touch, toCoordinateFromView: self.map)
let touchAnnotation = MKPointAnnotation()
touchAnnotation.coordinate = touchCoordinate
touchAnnotation.title = "You tapped here"
touchAnnotation.subtitle = "It is a recommended place"
self.map.addAnnotation(touchAnnotation)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The Simulator:
UILongPressGestureRecognizer is getting fired twice when user long presses on a map for over 2-4 seconds. How can I ensure it will only be fired once?
func action(gestureRecognizer:UIGestureRecognizer) {
println("long pressed on map")
override func viewDidLoad() {
super.viewDidLoad()
manager = CLLocationManager()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
if activePlace == -1 {
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
} else {
var uilpgr = UILongPressGestureRecognizer(target: self, action: "action:")
uilpgr.minimumPressDuration = 2.0
myMap.addGestureRecognizer(uilpgr)
}
}
func action(gestureRecognizer:UIGestureRecognizer) {
println("long pressed on map")
var touchPoint = gestureRecognizer.locationInView(self.myMap)
var newCoordinate = myMap.convertPoint(touchPoint, toCoordinateFromView: self.myMap)
var annotation = MKPointAnnotation()
annotation.coordinate = newCoordinate
//annotation.title = "New Place"
myMap.addAnnotation(annotation)
var loc = CLLocation(latitude: newCoordinate.latitude, longitude: newCoordinate.longitude)
}
You have to check the gesture recognizer´s state for the begin of the gesture:
func action(gestureRecognizer:UIGestureRecognizer) {
if gestureRecognizer.state == UIGestureRecognizerState.Began {
// ...
}
}
Long-press gestures are continuous. The gesture begins (UIGestureRecognizerStateBegan) when the number of allowable fingers (numberOfTouchesRequired) have been pressed for the specified period (minimumPressDuration) and the touches do not move beyond the allowable range of movement (allowableMovement). The gesture recognizer transitions to the Change state whenever a finger moves, and it ends (UIGestureRecognizerStateEnded) when any of the fingers are lifted.
try something like this:
let longGesture = UILongPressGestureRecognizer(target : self,
action : #selector(someFunc(gestureRecognizer:)))
func someFunc(gestureRecognizer: UILongPressGestureRecognizer){
if gestureRecognizer.state == .began {
//do something
}