MKCircle-Adding/Removing overlays - ios

Imagine that I have 6 circles. My timer calls first 2 circles in the beginning and overlay them on map view. Then after 2 seconds, it calls other circles and add them on map view. My problem is how to remove previous overlays.I want to see smooth transition for instance radar map.
Make it short, it would like to remove previous overlays and add the new overlays without flickering! Thanks a lot in advance!!.
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize mapView;
#synthesize timer;
#synthesize circle1,circle2,circle3,circle4,circle5,circle6;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
CLLocationCoordinate2D zoomLocation1;
zoomLocation1.latitude=29.830071;
zoomLocation1.longitude=-95.319099;
circle1 = [MKCircle circleWithCenterCoordinate:zoomLocation1 radius:15000];
circle1.title=#"first level";
CLLocationCoordinate2D zoomLocation2;
zoomLocation2.latitude=29.830071;
zoomLocation2.longitude=-95.319099;
circle2 = [MKCircle circleWithCenterCoordinate:zoomLocation2 radius:4000];
circle2.title=#"first level";
CLLocationCoordinate2D zoomLocation3;
zoomLocation3.latitude=29.830071;
zoomLocation3.longitude=-95.319099;
circle3 = [MKCircle circleWithCenterCoordinate:zoomLocation3 radius:6000];
circle3.title=#"second level";
CLLocationCoordinate2D zoomLocation4;
zoomLocation4.latitude=29.830071;
zoomLocation4.longitude=-95.319099;
circle4 = [MKCircle circleWithCenterCoordinate:zoomLocation4 radius:18000];
circle4.title=#"second level";
CLLocationCoordinate2D zoomLocation5;
zoomLocation5.latitude=29.830071;
zoomLocation5.longitude=-95.319099;
circle5 = [MKCircle circleWithCenterCoordinate:zoomLocation5 radius:1000];
circle5.title=#"third level";
CLLocationCoordinate2D zoomLocation6;
zoomLocation6.latitude=29.830071;
zoomLocation6.longitude=-95.319099;
circle6 = [MKCircle circleWithCenterCoordinate:zoomLocation6 radius:13000];
circle6.title=#"third level";
MKCoordinateRegion viewRegion=MKCoordinateRegionMakeWithDistance(zoomLocation1, 60*1609, 60*1609);
MKCoordinateRegion adjustedRegion=[mapView regionThatFits:viewRegion];
[mapView setRegion:adjustedRegion animated:YES];
mapView.mapType=MKMapTypeStandard;
[mapView setDelegate:(id)self];
i=0;
}
- (void)viewDidUnload
{
[self setMapView:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (IBAction)drawButton:(id)sender {
timer = [NSTimer scheduledTimerWithTimeInterval:(2.0) target:self selector:#selector(addingOverlay) userInfo:nil repeats:YES];
}
- (void)addingOverlay {
i=i+1;
switch(i%3)
{
case 1:
[mapView removeOverlays: [mapView overlays]];
[mapView addOverlay:circle1];
[mapView addOverlay:circle2];
break;
case 2:
[mapView removeOverlays: [mapView overlays]];
[mapView addOverlay:circle3];
[mapView addOverlay:circle4];
break;
case 3:
[mapView removeOverlays: [mapView overlays]];
[mapView addOverlay:circle5];
[mapView addOverlay:circle6];
break;
}
}
-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay> )overlay
{
MKCircleView *circleView = [[MKCircleView alloc] initWithOverlay:overlay];
if ([overlay.title isEqualToString:#"first level"])
{
circleView.strokeColor = [UIColor redColor];
circleView.lineWidth = 2;
circleView.fillColor=[UIColor yellowColor];
}
else if([overlay.title isEqualToString:#"second level"])
{
circleView.strokeColor = [UIColor whiteColor];
circleView.lineWidth = 2;
circleView.fillColor=[UIColor blackColor];
}
else{
circleView.strokeColor = [UIColor greenColor];
circleView.lineWidth = 2;
circleView.fillColor=[UIColor redColor];
}
return circleView;
}
#end

I just posted this answer to a similar question.
It has a function that I use (drawRangeRings) that draws two circles. In my example, both circles have the same center coordinate, but different radius values, and colors. You could easily change the code to use different center coordinates for each circle if you need that.
Basically, I call drawRangeRings when the center coordinate changes, and it removes the original two circles, and draws two new ones at a different location.
Let me know if this isn't exactly what you needed.
I don't see any flicker when I use this in my app.

Related

Resizing MKCircle on MKMapView is flickering

I'm trying to solve a problem since few days and I've got no good results. I've got a point and a circle on MKMapView. I've got UISlider and want to change size of MKCircle. Size is changed but during resizing this circle flickers and blinks.
Here is my code:
#implementation ViewController {
Annotation *_annotation;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_annotation = [[Annotation alloc] init];
[_annotation setCoordinate: CLLocationCoordinate2DMake(0, 0)];
[self.mapView addAnnotation:_annotation];
[self.mapView setCenterCoordinate:_annotation.coordinate animated:YES];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(_annotation.coordinate, 1000, 1000);
[self.mapView setRegion:region];
[self _addCircleOnCurrentLocationWithRadius:_slider.value];
}
- (IBAction)onSliderChanged:(UISlider *)sender {
[self.mapView removeOverlays:self.mapView.overlays];
[self _addCircleOnCurrentLocationWithRadius:sender.value];
}
- (void)_addCircleOnCurrentLocationWithRadius:(CGFloat)radius {
MKCircle *circle = [MKCircle circleWithCenterCoordinate:_annotation.coordinate radius:radius];
[self.mapView addOverlay:circle level:MKOverlayLevelAboveRoads];
}
#pragma mark - MKMapViewDelegate
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
MKPinAnnotationView *view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"Annotation"];
return view;
}
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay {
MKCircleView *view = [[MKCircleView alloc] initWithCircle:overlay];
view.fillColor = [UIColor redColor];
view.strokeColor = [UIColor blueColor];
view.alpha = 0.3;
return view;
}
#end
I've tried:
removing old overlays and adding new one,
doing as above with NSOperationQueue
Here is a screen recording how it looks like.
I see that is possible to do, Apple did this in Reminders app.
Also I'm familiar with following topics:
Smooth resizing of MKCircle
Moving MKCircle on MKMapview and dragging MKMapview
Smooth resizing of MKCircle
Thank you in advance.
Edit
I've did it. I will add an answer with class which supports resizing today or tomorrow.
I've did it by creating subclass of MKCircleView and override - (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)ctx method. TSCircleView class is shared on github here..

Objective-C Mapview only works once

I have a problem with my mapview. When I go to the view it works but when I go to another view and go back to the mapview it doesn't work anymore. I get the right latitude and longitude when I NSLog() it, so that is not the problem.
Code:
- (void) viewDidAppear:(BOOL)animated {
if(IS_IPHONE5) {
mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0,300,320,300)];
} else {
mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0,250,320,250)];
}
mapView.mapType = MKMapTypeStandard;
CLLocationCoordinate2D coord = {.latitude = [testLatitude floatValue], .longitude = [testLongitude floatValue]};
MKCoordinateSpan span = {.latitudeDelta = 0.05, .longitudeDelta = 0.05};
MKCoordinateRegion region = {coord, span};
[mapView setRegion:region];
[self.view addSubview:mapView];
CLLocationCoordinate2D annotationCoord;
annotationCoord.latitude = [testLatitude floatValue];
annotationCoord.longitude = [testLongitude floatValue];
MKPointAnnotation *annotationPoint = [[MKPointAnnotation alloc] init];
annotationPoint.coordinate = annotationCoord;
annotationPoint.title = stringTitle;
[mapView addAnnotation:annotationPoint];
}
based on what you've shown you keep adding map views (a new one each time the VC appears)
you seem to have forgotten to remove it!?
why are you allocating your mapview in viewdidAppear? it will add you map view multiple times on the screen if it is not removed on viewdidDisAppear
In your case you are not removing the map view that is already added , what you can do is use
- (void)viewWillAppear Method to Remove the map like that
- (void)viewWillAppear:(BOOL)animated
{
if(map != nil)
{
[map removeFromSuperview];
}
}
it will removed the previous map view before adding the new map
Add this code to your view:
-(void)viewDidDisappear:(BOOL)animated {
if(mapView)
{
[mapView removeFromSuperview];
}
}
}
Also to remove all subviews:
-(void)viewWillAppear:(BOOL)animated {
for (UIView *view in self.subviews) {
[view removeFromSuperview];
}
}
Try like this it may help
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:(BOOL)animated];
if(IS_IPHONE5) {
if (mapView == nil){
mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0,300,320,300)];
[mapview setDelegate:self];
}
} else {
if (mapView == nil){
mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0,250,320,250)];
[mapview setDelegate:self];
}
}
//Add ramaining Code
}
viewDidDisappear Code
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:(BOOL)animated];
[mapView removeFromSuperview];
mapView = nil;
}
ViewDidAppear: if mapview nil we are creating the mapview otherwise not
viewDidDisappear:if we go outside view we are removing the map

customizing map overlays in Xcode

I have a mapView with 2 circles. When I try to customize the large one the small one seems to follow the large one. For example if I customize the fill color for the large one the smaller one gets the same color. How do I make the smaller one a different color? Note: I use reusable identifiers. Thank you.. this is my working code but when i try to edit my smaller circle, the one with radius 100 it doesn't. note: this is my WORKING code as anything else i tried it failed.
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay {
static NSString *CircleOverlayIdentifier = #"Circle";
_mapView.delegate = self;
if ([overlay isKindOfClass:[CircleOverlay class]]) {
CircleOverlay *circleOverlay = (CircleOverlay *)overlay;
MKCircleView *annotationView =
(MKCircleView *)[mapView dequeueReusableAnnotationViewWithIdentifier:CircleOverlayIdentifier];
if (!annotationView) {
MKCircle *circle = [MKCircle
circleWithCenterCoordinate:circleOverlay.coordinate
radius:circleOverlay.radius];
annotationView = [[MKCircleView alloc] initWithCircle:circle];
//this one
}
if (overlay == self.targetOverlay) {
//adjustable
annotationView.fillColor = [UIColor colorWithRed:1.0f green:0.0f blue:0.0f alpha:0.3f];
annotationView.strokeColor = [UIColor redColor];
annotationView.lineWidth = 1.0f;
} else {
//fixed
annotationView.fillColor = [UIColor colorWithWhite:0.3f alpha:0.3f];
annotationView.strokeColor = [UIColor purpleColor];
annotationView.lineWidth = 2.0f;
}
return annotationView;
}
return nil;
}
- (void)configureOverlay {
if (self.location) {
[self.mapView removeAnnotations:[self.mapView annotations]];
[self.mapView removeOverlays:[self.mapView overlays]];
CircleOverlay *overlaysmall = [[CircleOverlay alloc] initWithCoordinate:self.location.coordinate radius:100];
[self.mapView addOverlay:overlaysmall];
_targetOverlaySmall = overlaysmall;
CircleOverlay *overlay = [[CircleOverlay alloc] initWithCoordinate:self.location.coordinate radius:self.radius];
[self.mapView addOverlay:overlay];
GeoQueryAnnotation *annotation = [[GeoQueryAnnotation alloc] initWithCoordinate:self.location.coordinate radius:self.radius];
[self.mapView addAnnotation:annotation];
[self updateLocations];
}
}

Open different websites through pinpoints in mapview

My idea is to have several (maybe as much as 100) pinpoints in a Google map.
I have created a button in the annotation message that appears when I click the pin. I want each pinpoint to be connected to a website. A different website for each pinpoint.
But at the moment I use this:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control
{
[[UIApplication sharedApplication]
openURL:[NSURL URLWithString: #"http://www.google.co.uk"]];
The problem is that with this code, all the pinpoints open the same website.
Is there any way I can specify a web address for each pinpoint?
#import "ViewController.h"
#import "NewClass.h"
#implementation ViewController
#synthesize mapview;
NSString *myString;
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
//[[UIApplication sharedApplication]
//openURL:[NSURL URLWithString: #"http://www.arebikepark.se"]];
NewClass *ann = (NewClass *)view.annotation;
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:ann.website]];
}
-(IBAction)getLocation {
mapview.showsUserLocation = YES;
}
-(IBAction)setMap:(id)sender {
switch (((UISegmentedControl *) sender).selectedSegmentIndex) {
case 0:
mapview.mapType = MKMapTypeStandard;
break;
case 1:
mapview.mapType = MKMapTypeSatellite;
break;
case 2:
mapview.mapType = MKMapTypeHybrid;
break;
default:
break;
}
}
- (void)viewDidLoad {
[super viewDidLoad];
[mapview setMapType:MKMapTypeStandard];
[mapview setZoomEnabled:YES];
[mapview setScrollEnabled:YES];
MKCoordinateRegion region = { {0.0, 0.0 }, {0.0, 0.0 } };
region.center.latitude = 63.399785761795506;
region.center.longitude = 12.91691780090332;
region.span.longitudeDelta = 100.0f;
region.span.latitudeDelta = 100.0f;
[mapview setRegion:region animated:YES];
NewClass *ann = [[NewClass alloc] init];
ann.title = #"Åre";
ann.subtitle = #"www.arebikepark.se";
ann.coordinate = region.center;
ann.website = #"arebikepark.se";
[mapview addAnnotation:ann];
MKCoordinateRegion region1 = { {0.0, 0.0 }, {0.0, 0.0 } };
region1.center.latitude = 61.717050948488904;
region1.center.longitude = 16.153764724731445;
region1.span.longitudeDelta = 100.0f;
region1.span.latitudeDelta = 100.0f;
[mapview setRegion:region1 animated:YES];
NewClass *ann1 = [[NewClass alloc] init];
ann1.title = #"Järvsö";
ann1.subtitle = #"www.jarvsobergscykelpark.se";
ann1.coordinate = region1.center;
ann.website = #"www.jarvsobergscykelpark.se";
[mapview addAnnotation:ann1];
MKCoordinateRegion region2 = { {0.0, 0.0 }, {0.0, 0.0 } };
region2.center.latitude = 57.84191869696362;
region2.center.longitude = 12.02951431274414;
region2.span.longitudeDelta = 100.0f;
region2.span.latitudeDelta = 100.0f;
[mapview setRegion:region2 animated:YES];
NewClass *ann2 = [[NewClass alloc] init];
ann2.title = #"Ale";
ann2.subtitle = #"www.alebikepark.se";
ann2.coordinate = region2.center;
[mapview addAnnotation:ann2];
MKCoordinateRegion region3 = { {0.0, 0.0 }, {0.0, 0.0 } };
region3.center.latitude = 61.17768100166834;
region3.center.longitude = 13.261871337890625;
region3.span.longitudeDelta = 100.0f;
region3.span.latitudeDelta = 100.0f;
[mapview setRegion:region3 animated:YES];
NewClass *ann3 = [[NewClass alloc] init];
ann3.title = #"Kläppen";
ann3.subtitle = #"www.klappen.se/sv/Sommar/Bikepark";
ann3.coordinate = region3.center;
ann3.website = #"www.klappen.se/sv/Sommar/Bikepark";
[mapview addAnnotation:ann3];
MKCoordinateRegion region4 = { {0.0, 0.0 }, {0.0, 0.0 } };
region4.center.latitude = 65.82881853569008;
region4.center.longitude = 15.067813396453857;
region4.span.longitudeDelta = 100.0f;
region4.span.latitudeDelta = 100.0f;
[mapview setRegion:region4 animated:YES];
NewClass *ann4 = [[NewClass alloc] init];
ann4.title = #"Hemavan";
ann4.subtitle = #"www.bikepark.nu";
ann4.coordinate = region4.center;
ann4.website = #"www.bikepark.nu";
[mapview addAnnotation:ann4];
MKCoordinateRegion region5 = { {0.0, 0.0 }, {0.0, 0.0 } };
region5.center.latitude = 63.29058608431198;
region5.center.longitude = 18.7042236328125;
region5.span.longitudeDelta = 100.0f;
region5.span.latitudeDelta = 100.0f;
[mapview setRegion:region5 animated:YES];
NewClass *ann5 = [[NewClass alloc] init];
ann5.title = #"Örnsköldsvik";
ann5.subtitle = #"www.hkbikepark.se";
ann5.coordinate = region5.center;
ann5.website = #"www.hkbikepark.se";
[mapview addAnnotation:ann5];
}
-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
MKPinAnnotationView *MyPin =(MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"MyPin"];
if (!MyPin) {
MyPin = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"MyPin"];
MyPin.pinColor = MKPinAnnotationColorRed;
MyPin.canShowCallout = YES;
UIButton *details = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
MyPin.rightCalloutAccessoryView = details;
MyPin.canShowCallout = YES;
}
return MyPin;
}
#end
Add a property to your custom annotation class (the one that implements MKAnnotation) that stores the annotation's website. Set this property when you add the annotation to the map:
ann.website = #"http://www.google.co.uk";
[mapView addAnnotation:ann];
Then in the calloutAccessoryControlTapped delegate method, you can access this property:
MyAnnotationClass *ann = (MyAnnotationClass *)view.annotation;
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:ann.website]];
Based on the additional code you posted, the problem is that the website url strings don't have the http:// scheme prefix which the URLWithString method needs to convert the string to a URL. Because the url strings are invalid, the method returns nil which results in the openURL method doing nothing.
So, for example, the lines that set the website property should be:
ann.website = #"http://www.arebikepark.se";
ann1.website = #"http://www.jarvsobergscykelpark.se";
ann2.website = #"http://www.alebikepark.se"; //(line was missing completely)
ann3.website = #"http://www.klappen.se/sv/Sommar/Bikepark";
ann4.website = #"http://www.bikepark.nu";
ann5.website = #"http://www.hkbikepark.se";
Additionally, though the above urls are not a problem, if you intend to use urls with query strings that might contain special characters, you'll also need to escape the url string before passing it. One way is to use the stringByAddingPercentEscapesUsingEncoding method. See this answer by Dave DeLong for an example and some precautions/limitations/alternatives with that method.
Finally, unrelated to your main issue but, here are some additional comments on the code:
You don't need to create a region for each annotation just to set its coordinate. You can just do ann.coordinate = CLLocationCoordinate2DMake(63.399785761795506, 12.91691780090332);
If you are not using ARC, there are several memory leaks.
In viewForAnnotation, you should add an else part to the if statement: else MyPin.annotation = annotation; in case an annotation view is re-used for a different annotation. It probably won't happen with so few annotations but it's logically and technically required.
If you want the user location to appear as a blue dot instead of a red pin, you'll need to add this to the top of the viewForAnnotation method: if ([annotation isKindOfClass:[MKUserLocation class]]) return nil;

MKPolyline only shown when map is moved

I have a MKMapView with some pins on it. I connect the pins with a MKPolyline view. But the MKPolyline is only shown when I move the map (when the MapView is updated?). I want to see the MKPolyline from the beginning on.
Please inspect the following code:
-(void)plotSnapPosition {
for (id<MKAnnotation> annotation in myMapView.annotations) {
[myMapView removeAnnotation:annotation];
}
for (id<MKOverlay> overlay in myMapView.overlays) {
[myMapView removeOverlay:overlay];
}
NSArray *snaps = self.entry.snapsArray;
CLLocationCoordinate2D *locations = malloc(sizeof(CLLocationCoordinate2D) * snaps.count);
NSInteger counter = 0;
for (Snap *snap in snaps) {
locations[counter] = [snap coordinates];
CLLocationCoordinate2D c = [snap coordinates];
CAHAnnotation *annotation = [[CAHAnnotation alloc] initWithDate:snap.timeAsString coordinate:c counter:counter];
[myMapView addAnnotation:annotation];
counter++;
}
MKPolyline *polyline = [MKPolyline polylineWithCoordinates:locations count:snaps.count];
MKPolylineView *routeLineView = [[MKPolylineView alloc] initWithPolyline:polyline];
routeLineView.fillColor = [UIColor redColor];
routeLineView.strokeColor = [UIColor redColor];
routeLineView.lineWidth = 5;
[myMapView setVisibleMapRect:polyline.boundingMapRect];
[self.myMapView addOverlay:polyline];
}
-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay {
if ([overlay isKindOfClass:[MKPolyline class]]) {
MKPolylineView *routeLineView = [[MKPolylineView alloc] initWithPolyline:overlay];
routeLineView.fillColor = [UIColor blueColor];
routeLineView.strokeColor = [UIColor blueColor];
routeLineView.lineWidth = 3;
return routeLineView;
}
return nil;
}
For testing issues I have set the color of the MKPolyline in the method -(void)plotSnapPosition to red. In the delegate I set it to blue. Only the blue one is shown, after moving the map around.
can someone help me out of this? I think it is only a small mistake. Thank you.
here are the screenshots:
the two pins
after moving the map:
the path after moving the map
Make sure you set the mapView's delegate before adding the overlay. So, in your case
mapView.delegate = self;
[self plotSnapPosition];
Have you tried adding a [overlayView setNeedsDisplay] call when you finish drawing?

Resources