I'm trying to add a blur effect on a overlay on a map. I actually need the blur effect over a circle on the over the map, the method I use to get that is not that important.
I have a class that extends from MKCircleRenderer
and I wanted to add a blur effect over the map that it covers.
I was trying using the -fillPath:inContext:
method, but my ignorance over Core Graphics and Core Image lead me to nowhere and I'm really really lost about this issue.
My attempt was using the CIFilter
and for that I needed a CIImage
which I tried to create from the context. But I found no way to create a CGBitmapContext
, CGImage
nor any other class from the context. Any method I tried resulted on NULL with no further details about why. I can't remember all I tried so I'm sorry about not pointing anything about that.
My class currently implements one methods that does not do really much:
- (instancetype)initWithOverlay:(id<MKOverlay>)overlay {
if (self = [super initWithOverlay:overlay]) {
self.strokeColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:1];
self.fillColor = [UIColor colorWithRed:0.4 green:0.2 blue:0.2 alpha:0.1];
self.lineWidth = 1;
}
return self;
}
An alternative could be using a custom MKAnnotation
and add the blur effect over the view with the UIVisualEffectView
. The hard part with this approach is increasing/decreasing the size when zooming.
This should work on iOS 8+
Edit
In this case, the map behind the inside of the circle should be blurred
So I ended up using a UIVisualEffectView
on top of the overlay. The trick was in using a CADisplayLink
for keeping the view on place.
Here is some code example that does the job (it ignores a few things that should be taken in consideration when actually doing this on an app, like removing the link, keeping track that what is done on viewDidAppear has to be paired with probably symmetrical work on viewWillDissapear or something, I could use viewDidLoad I think, but did it this way when testing).
#import "ViewController.h"
#import <MapKit/MapKit.h>
#import <QuartzCore/QuartzCore.h>
@interface ViewController () {
IBOutlet MKMapView *map;
UIView *ov;
MKCircle *c;
UIVisualEffectView *ev;
}
@end
@implementation ViewController
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(40.7828647,-73.9675438);
c = [MKCircle circleWithCenterCoordinate:center radius:1000];
[map addOverlay:c];
MKCoordinateSpan span = MKCoordinateSpanMake(0.07, 0.07);
MKCoordinateRegion region = MKCoordinateRegionMake(center, span);
region = [map regionThatFits:region];
[map setRegion:region animated:YES];
ov = [[UIView alloc] init];
ov.translatesAutoresizingMaskIntoConstraints = NO;
ov.backgroundColor = [UIColor clearColor];
ov.clipsToBounds = YES;
ov.layer.borderWidth = 1;
ov.layer.borderColor = [UIColor blackColor].CGColor;
[map addSubview:ov];
UIBlurEffect *blur = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
ev = [[UIVisualEffectView alloc] initWithEffect:blur];
[ov addSubview:ev];
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(update:)];
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)update:(CADisplayLink *)link {
ov.frame = [map convertRegion:MKCoordinateRegionForMapRect(c.boundingMapRect) toRectToView:map];
ov.layer.cornerRadius = ov.frame.size.height / 2;
ev.frame = CGRectMake(0, 0, ov.frame.size.width, ov.frame.size.height);
}
@end
Edit: Screenshot