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
There are two ways to do that.
One is simple, you can just use Image.
You might find the following images useful:
and the code to use them in viewForAnnotation:
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
// ... get the annotation delegate and allocate the MKAnnotationView (annView)
if ([annotationDelegate.type localizedCaseInsensitiveCompare:@"NeedsBluePin"] == NSOrderedSame)
{
UIImage * image = [UIImage imageNamed:@"blue_pin.png"];
UIImageView *imageView = [[[UIImageView alloc] initWithImage:image] autorelease];
[annView addSubview:imageView];
}
}
Other method do that programmatically [make circle yourself]
This code can help you.
import UIKit
import AddressBook
import AddressBookUI
import MapKit
import CoreLocation
import MessageUI
import Social
class ViewController: UIViewController, ABPeoplePickerNavigationControllerDelegate, MFMailComposeViewControllerDelegate, MKMapViewDelegate {
@IBOutlet weak var name: UILabel!
@IBOutlet weak var email: UILabel!
@IBOutlet weak var photo: UIImageView!
@IBOutlet weak var map: MKMapView!
let locMan:CLLocationManager=CLLocationManager()
// Blurring Code
@IBOutlet weak var labelBackground: UIView!
var backgroundBlur: UIVisualEffectView!
@IBAction func newBFF(sender: AnyObject) {
let picker: ABPeoplePickerNavigationController =
ABPeoplePickerNavigationController()
picker.peoplePickerDelegate = self
presentViewController(picker, animated: true, completion: nil)
}
@IBAction func sendEmail(sender: AnyObject) {
var emailAddresses:[String]=[self.email.text!]
var mailComposer:MFMailComposeViewController =
MFMailComposeViewController()
mailComposer.mailComposeDelegate=self;
mailComposer.setToRecipients(emailAddresses)
presentViewController(mailComposer, animated: true, completion: nil)
}
func mailComposeController(controller: MFMailComposeViewController!,
didFinishWithResult result: MFMailComposeResult, error: NSError!) {
dismissViewControllerAnimated(true, completion: nil)
}
func peoplePickerNavigationController(peoplePicker: ABPeoplePickerNavigationController!, didSelectPerson person: ABRecord!) {
let friendName:String = ABRecordCopyValue(person, kABPersonFirstNameProperty).takeRetainedValue() as String as String
name.text=friendName
let friendAddressSet:ABMultiValueRef = ABRecordCopyValue(person, kABPersonAddressProperty).takeRetainedValue()
if ABMultiValueGetCount(friendAddressSet)>0 {
let friendFirstAddress: Dictionary = ABMultiValueCopyValueAtIndex(friendAddressSet, 0).takeRetainedValue() as NSDictionary
showAddress(friendFirstAddress)
}
let friendEmailAddresses:ABMultiValueRef = ABRecordCopyValue(person, kABPersonEmailProperty).takeRetainedValue()
if ABMultiValueGetCount(friendEmailAddresses)>0 {
let friendEmail: String = ABMultiValueCopyValueAtIndex(friendEmailAddresses, 0).takeRetainedValue() as String
email.text=friendEmail
}
if ABPersonHasImageData(person) {
photo.image = UIImage(data: ABPersonCopyImageData(person).takeRetainedValue())
}
}
func showAddress(fullAddress:NSDictionary) {
let geocoder: CLGeocoder = CLGeocoder()
geocoder.geocodeAddressDictionary(fullAddress, completionHandler:
{(placemarks: [AnyObject]!, error: NSError!) -> Void in
let friendPlacemark:CLPlacemark = placemarks[0] as CLPlacemark
let mapRegion:MKCoordinateRegion =
MKCoordinateRegion(center: friendPlacemark.location.coordinate,
span: MKCoordinateSpanMake(0.2, 0.2))
self.map.setRegion(mapRegion, animated: true)
let mapPlacemark: MKPlacemark = MKPlacemark(placemark: friendPlacemark)
self.map.addAnnotation(mapPlacemark)
})
}
func mapView(aMapView: MKMapView!,
viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
let pinDrop:MKPinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myspot")
pinDrop.animatesDrop=true
pinDrop.canShowCallout=true
pinDrop.pinColor=MKPinAnnotationColor.Purple
return pinDrop
}
@IBAction func sendTweet(sender: AnyObject) {
let geocoder: CLGeocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(map.userLocation.location, completionHandler:
{(placemarks: [AnyObject]!, error: NSError!) -> Void in
let myPlacemark:CLPlacemark = placemarks[0] as CLPlacemark
let tweetText:String =
"Hello all - I'm currently in \(myPlacemark.locality)!"
let tweetComposer: SLComposeViewController =
SLComposeViewController(forServiceType: SLServiceTypeTwitter)
if SLComposeViewController.isAvailableForServiceType(SLServiceTypeTwitter) {
tweetComposer.setInitialText(tweetText)
self.presentViewController(tweetComposer, animated: true, completion: nil)
}
})
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//let locMan:CLLocationManager=CLLocationManager()
locMan.requestWhenInUseAuthorization()
let blur: UIBlurEffect = UIBlurEffect(style: UIBlurEffectStyle.Light)
backgroundBlur = UIVisualEffectView (effect: blur)
backgroundBlur.frame = labelBackground.frame
view.insertSubview(backgroundBlur, belowSubview: labelBackground)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return UIStatusBarStyle.LightContent
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With