I have an MKMapView that has a MKTileOverlay so that I can show Open Street Map tiles:
NSString *templateURL = #"http://tile.openstreetmap.org/{z}/{x}/{y}.png";
self.tileOverlay = [[MKTileOverlay alloc] initWithURLTemplate:templateURL];
self.tileOverlay.canReplaceMapContent = YES;
[self.mapView addOverlay:self.tileOverlay level:MKOverlayLevelAboveLabels];
I also want to show an MKPolyline from my current location to Apple Park in Cupertino. This polyline needs to be updated as I move, and since an MKPolyline object isn't mutable, I have to remove it and add it for each location update:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray*)locations {
self.currentLocation = userLocation;
// Update polyline
CLLocationCoordinate2D applePark = CLLocationCoordinate2DMake(37.334626, -122.008895);
[self buildPolylineWithDestinationLocation:applePark];
}
- (void)buildPolylineWithDestinationLocation:(CLLocationCoordinate2D)coordinate {
// Remove the polyline each time so we can redraw it
if (self.polylineApple) {
[self.mapView removeOverlay:self.polylineApple];
}
// Get current location
CLLocation *location = self.currentLocation;
CLLocationCoordinate2D currentLocation = location.coordinate;
CLLocationCoordinate2D points[2];
points[0] = currentLocation;
points[1] = coordinate;
// Remove all route polylines
MKPolyline *oldPolyline = self.polylineApple;
// Draw a line
self.polylineApple = [MKPolyline polylineWithCoordinates:points count:2];
[self.mapView addOverlay:self.polylineApple];
if (oldPolyline) {
[self.mapView removeOverlay:oldPolyline];
oldPolyline = nil;
}
}
The problem is, this used to work great in older versions of iOS, but ever since iOS 13 this has caused the tiles to be redrawn each time that MKPolyline is removed and added:
Is this just an iOS 13 bug, or is there something I need to fix in my code to make this not happen?
As I know since iOS 8, I have seen this issue. Not always but some times.
This issue is linked to 2 things:
1) The action every second to remove and add again a polyline will ask the MKMapView to redrawn the part with the polyline and as a consequence the MKTileOverlay below.
2) If the tile's size (in KB, not the resolution) is low, the issue may not be present.
The best advice I can give to you is to add you own view to the MKMapView and update this own view by calling setNeedsDisplay. It will trigger the draw method where you can convert the map points (latitude, longitude) to screen points and draw the line.
Edit: A link speaking about MKTileOverlay reload issue
https://forums.developer.apple.com/message/313677#313677
Related
I am using Google Map SDK for iOS. I am drawing polylines in Driving mode.
But when i stop,and Zoom google map then, my Current position cursor automatically moves and redraw zigzag polylines, due to that all previous polylines drawn get overlapped and polylines get completely changed.Same things happens when i go in background and drive.
Could i know why is it happening? And How can I draw smooth polylines in driving and walking mode same time in same path.
My Code-
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
pointString=[NSString stringWithFormat:#"%f,%f",newLocation.coordinate.latitude,newLocation.coordinate.longitude];
CLLocationDistance kilometers = [newLocation distanceFromLocation:oldLocation] / 1000;
NSLog(#"Distance Travelled in Kilometer :%f",kilometers);
[self.points addObject:pointString];
GMSMutablePath *path = [GMSMutablePath path];
for (int i=0; i<self.points.count; i++)
{
NSArray *latlongArray = [[self.points objectAtIndex:i]componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#","]];
[path addLatitude:[[latlongArray objectAtIndex:0] doubleValue] longitude:[[latlongArray objectAtIndex:1] doubleValue]];
}
if (self.points.count>2)
{
GMSPolyline *polyline = [GMSPolyline polylineWithPath:path];
polyline.strokeColor = [UIColor blueColor];
polyline.strokeWidth = 5.f;
polyline.map = mapView_;
self.mapContainerView = mapView_;
}
}
If , I remain in Same position, then Googme map Cursor position automaticalaly moves and draw polylines like this.
add a NSLocationAlwaysUsageDescription and a UIBackgroundModes -> "location" to Info.plist
AND
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9) {
manager.allowsBackgroundLocationUpdates = YES;
}
Before allowing background location updtaes:
enter image description here
After Allowing background location updtaes:
enter image description here
Most of this itinerary has been drawn in the background.
Two things are going on. First, the GPS chip does not always return the same location when standing still. The determined GPS location always fluctuates a bit. iOS does an effort to detect that you're standing still, and then supply the same location, but I think that is done to a lesser extend in Driving mode.
Second, by using the convoluted way to store the samples as strings, you go through a %f conversion, which looses accuracy. That can exaggerate any differences between locations. If you use the CLLocation objects directly, you're likely getting a better result (and much cleaner code):
[self.points addObject:newLocation];
GMSMutablePath *path = [GMSMutablePath path];
for (CLLocation *col in self.points)
{
[path addLatitude:col.latitude longitude:col.longitude];
}
Also, make sure you set the correct settings on the CLLocationManager:
theLocationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
theLocationManager.distanceFilter = kCLDistanceFilterNone;
theLocationManager.activityType = CLActivityTypeOtherNavigation;
theLocationManager.allowsBackgroundLocationUpdates = YES
One other thing. It is also very strange that you change the view in the didUpdateToLocation: method:
self.mapContainerView = mapView_;
You should just use setNeedsDisplay on the existing view, after updating the path.
I am working with mapkit in Xcode 5.1 and am trying to display the map scale in regionDidChangeAnimated. I have no idea now to accomplish this though. I tried to look around and was unsuccessful. Any ideas?
EDIT:
-(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
CGPoint nePoint = CGPointMake(self.mapView.bounds.origin.x + mapView.bounds.size.width, mapView.bounds.origin.y);
CGPoint swPoint = CGPointMake((self.mapView.bounds.origin.x), (mapView.bounds.origin.y + mapView.bounds.size.height));
CLLocationCoordinate2D neCoord;
neCoord = [self.mapView convertPoint:nePoint toCoordinateFromView:self.mapView];
CLLocationCoordinate2D swCoord;
swCoord = [self.mapView convertPoint:swPoint toCoordinateFromView:self.mapView];
CLLocationDistance distance = [neCoord distanceFromLocation:swCoord];
}
Any reason why I am getting an error with the last line, CLLocationDistance?
Use the methods that translate between the coordinates on the map and the points on the map view, such as convertPoint:toCoordinateFromView:. Use the edge points of your view for this.
Now you have the coordinates - you can calculate the distances between the points with CLLocation's distanceFromLocation:. You can make some assumptions about the width of your view based on the physical properties of, day an iPhone or iPad and calculate the scale.
I am using the HGMovingAnnotation and HGMovingAnnotationView code off of github to animate a MKAnnotation on an MKmap. When I run the example project from HG project everything works fine.
I have altred the original HG project to allow me to manually push a new coordinate to the HGMapPath and then move the annotation where I want it.
I have placed a button, for testing, to run the manual process and everything works fine. The annotation moves around the screen. The issue is, when I try to now call this manual method with data from a live socket.io connection, the map annotation won't move.
Also, when the map first loads the annotation won't show up until I move the map a little bit. The same thing for the moving annotation manually, it won't show the movement from the stream of data, until I zoom the map. But if I do the push button way, avoiding the io stream, the annotation moves without needing to zoom or pan the map?
PLACING THE VIEW ANNOTATIONS
if(doubleLat && doubleLng) {
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(doubleLat, doubleLng);
//Create path object
self.assignedAmbPath = [[HGMapPath alloc] initWithCoordinate:coordinate];
HGMovingAnnotation *movingObject = [[HGMovingAnnotation alloc] initWithMapPath:self.assignedAmbPath];
self.movingAssignedAmbObject = movingObject;
// add the annotation to the map
[self.mapView addAnnotation:movingObject];
// zoom the map around the moving object
MKCoordinateSpan span = MKCoordinateSpanMake(0.01, 0.01);
MKCoordinateRegion region = MKCoordinateRegionMake(MKCoordinateForMapPoint(self.movingAssignedAmbObject.currentLocation), span);
[self.mapView setRegion:region animated:YES];
// start moving the object
[movingObject start];
}
CODE THAT WORKS
- (IBAction)testMoveBtnPressed:(id)sender {
//TODO: move x and y
DLog(#"============== Test move button was pressed ================ ");
NSLog(#"");
int randn = (random() % 15)+15;
float pscale = (float)randn / 10000;
double lat = 39.9813855 + pscale;
double lng = -75.1502155 + pscale;
for (id<MKAnnotation> annotation in self.mapView.annotations){
MKAnnotationView* anView = [self.mapView viewForAnnotation: annotation];
if (![annotation isKindOfClass:[PhoneAnnotation class]]){
// Process annotation view
[((HGMovingAnnotation *)annotation) trackToNewPosition:CLLocationCoordinate2DMake(lat, lng)];
}
}
}
CODE THAT DOESN'T WORK
{
//TODO: move thing to new location
double doubleLat = [lat doubleValue];
double doubleLng = [lng doubleValue];
// NSLog(#"--------------- Jason it is ------------- Latitude being passed in is %f", doubleLat);
// NSLog(#"--------------- Jason it is ------------- Longitude being passed in is %f", doubleLng);
//
// [self.movingAssignedAmbObject trackToNewPosition:CLLocationCoordinate2DMake(doubleLat, doubleLng)];
for (id<MKAnnotation> annotation in self.mapView.annotations){
MKAnnotationView* anView = [self.mapView viewForAnnotation: annotation];
if (![annotation isKindOfClass:[PhoneAnnotation class]]){
// Process annotation view
[((HGMovingAnnotation *)annotation) trackToNewPosition:CLLocationCoordinate2DMake(doubleLat, doubleLng)];
}
}
}
The issue is the HG library though works as described doesn't work proper, unless your using the path, if you don't create the annotation with a coordinate attached, which it doesn't.
I have a function in my view controller with a mapkit that is called when the location changes. I would like set to the map view so that is is centered around the current location and moves with it as it updates.
It works in a sense i.e it tracks along, however it is always zoomed in really far. If I zoom out it snaps back to where it was when it calls the update again after getting a new location.
in from params CLLocation *loc
bool first = TRUE;
if(first){
first=FALSE; // bit of a bodge...if i set this to false it will track the update possition but not at the desired "zoom" its look far to close.
CLLocation *loc = [[CLLocation alloc] initWithLatitude:54.976619 longitude:-1.613118];//newcastle city center.
CLLocationDegrees spanLat = 1000;
CLLocationDegrees spanLng = 1000;
userLocation = MKCoordinateRegionMakeWithDistance(loc.coordinate, spanLat, spanLng);
[mapview setRegion:userLocation animated:TRUE];
}else {
CLLocationDegrees spanLat = [mapview region].span.latitudeDelta;// keep the originals? DOESN'T WORK!!
CLLocationDegrees spanLng = [mapview region].span.longitudeDelta;//
userLocation = MKCoordinateRegionMakeWithDistance(loc.coordinate, spanLat, spanLng);
[mapview setRegion:userLocation animated:TRUE];
}
Just keep setting the center coordinate and don't set the region.
By the way, you do know that you don't need any of that code, right? You can just tell the mapview to track the device's current location. See my book for an example:
http://www.apeth.com/iOSBook/ch34.html#_map_kit_and_current_location
I'm trying to add the distance from the user's position to a selected annotation's subtitle in a mapview. The mechanics of it are working, but the actual callout gets messed up the first time it's displayed. There appears to be a redraw problem.
Subsequent taps on the pin show the correct layout.
Here's the relevant code:
// called when selecting annotations
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view{
MKPointAnnotation *selectedAnnotation = view.annotation;
//attempt to add distance on annotation
CLLocation *pointALocation = [[CLLocation alloc]
initWithLatitude:selectedAnnotation.coordinate.latitude
longitude:selectedAnnotation.coordinate.longitude];
float distanceMeters = [pointALocation distanceFromLocation:locationManager.location];
//for sending info to detail
myPinTitle = selectedAnnotation.title;
[selectedAnnotation setSubtitle:[NSString stringWithFormat:#"%.2f miles away", (distanceMeters / 1609.344)]];
}
I've tried calling [view setNeedsDisplay], but to no avail.
Thanks in advance for your help.
The Solution that Worked
Here's the solution I finally came up with. It seems to work.
I edited out the duplicate code from the didSelectAnnotationView method, above, and came up with:
//called when user location changes
- (void)updatePinsDistance
{
for (int x=0; x< [[mapView annotations]count]; x++) {
MKPointAnnotation *thisPin =[[mapView annotations] objectAtIndex:x];
//attempt to add distance on annotation
CLLocation *pointALocation = [[CLLocation alloc]
initWithLatitude:thisPin.coordinate.latitude
longitude:thisPin.coordinate.longitude];
float distanceMeters = [pointALocation distanceFromLocation:locationManager.location];
NSString *distanceMiles = [NSString stringWithFormat:#"%.2f miles from you",
(distanceMeters / 1609.344)];
[thisPin setSubtitle:distanceMiles];
}
}
You should set your subtitle in another place than didSelectAnnotationView. Actually all annotationViews should have their title and subtitle set before they are returned by the mapView:viewForAnnotation: method.
The fact that you set a long subtitle certainly explains that the callout is not the right size. The size must be calculated before the didSelectAnnotationView is called.