Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add circle with blur effect on a MKMapView

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

like image 657
Matías Marquez Avatar asked Oct 21 '15 16:10

Matías Marquez


2 Answers

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

like image 186
Matías Marquez Avatar answered Nov 13 '22 03:11

Matías Marquez


There are two ways to do that.

One is simple, you can just use Image.

You might find the following images useful:

enter image description here enter image description here enter image description here enter image description here enter image description here

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
    }


}
like image 22
Grigori Jlavyan Avatar answered Nov 13 '22 01:11

Grigori Jlavyan