Black and white overlay for an MKMapView
Asked Answered
T

4

7

I have an MKMapView and am adding an overlay (MKOverlay) to it.

I would like the overlay to make the map view underneath to appear black & white (i.e. monochrome). Is there any way of doing this?

(I guess I could make the overlay translucent black/gray, but that's not the exact effect I would like.)


System details: developing an iOS 5 app in Xcode 4.2.

Toreutic answered 24/1, 2012 at 15:14 Comment(0)
E
8

The most proper route, which sadly isn't yet available on iOS would be:

Your MKOverlay is at some point being used to create an MKOverlayView, which will have a CALayer as it descends from UIView. You can attach CIFilters as compositingFilters to CALayers, which dictate how the view is composited with the background. You could attach a filter of type CIColorMonochrome to that.

Because that option isn't available, you're going to have to leap through some major hurdles to do the work for yourself. You'll probably need to implement your own custom MKOverlayView that in its drawView uses the techniques given in Apple's QA1703 to get the pixel contents of the map view in the relevant area (be careful not to end up in an infinite recursive loop though), transform those into black and white and present them as your view.

Is that something it's worth investigating further or would you be happier to substitute the effect than to really delve into this stuff?

Event answered 24/1, 2012 at 15:52 Comment(2)
Has anyone done this? Isnt making filters like a fucntion? isnt this fucntion only applied to the Overlay and not to the map beneath it?Longsighted
developer.apple.com/library/ios/#qa/qa1703/_index.html - link not working (Sorry, that page cannot be found.)Heterogenesis
P
4

I know the question is really old, and it may not match the criteria but for whom it is interesting, in SwiftUI you can modify the saturation? It worked for me, here an example:

Map(coordinateRegion: .constant(
    MKCoordinateRegion(center: CLLocationCoordinate2D(
                        latitude: 53.551086,
                        longitude: 9.993682),
                       span: MKCoordinateSpan(
                        latitudeDelta: 0.05,
                        longitudeDelta: 0.05))),
interactionModes: [])
.frame(width: 400, height: 300)
.saturation(0.0)
Presidency answered 7/8, 2021 at 11:34 Comment(0)
R
2

I needed same color manipulations on map, and I decided that making the whole map black and white may be better solution, here is how you can do this:

  1. created new imageCGcontex
  2. rendered map in new CGcontext
  3. got UIImage from current CGcontext
  4. applied CIFilter to UIImage and got CIImage as result
  5. rendered CIImage in CIContext and got CGImageRef as result
  6. created UIImage from CGImageRef
  7. added sub imageview to apple map view
  8. set result image to that imageView

Here is some dirty code to do this:

  self.drawContext = [[CIContext alloc] init];
  [self.monochromeSubView removeFromSuperview];

  UIGraphicsBeginImageContext(self.appleMapView.bounds.size);
  UIImage *mySourceImage = UIGraphicsGetImageFromCurrentImageContext();
  CIImage *newMyCiImage = [[CIImage alloc] initWithImage:mySourceImage];
  CIFilter *newMyMonochromeFilter = [CIFilter filterWithName:@"CIPhotoEffectTonal"];
  [newMyMonochromeFilter setValue:newMyCiImage forKey:kCIInputImageKey];
  CIImage *newMyfilteredCIImage = newMyMonochromeFilter.outputImage;
  
  CGImageRef myOutputImageRef = [self.drawContext createCGImage:newMyfilteredCIImage
                                                       fromRect:newMyfilteredCIImage.extent];
  UIImage *newMyImage = [[UIImage alloc] initWithCGImage:myOutputImageRef];

  self.monochromeSubView.image = newMyImage;
  [self.appleMapView addSubview:self.monochromeSubView];

  CGImageRelease(myOutputImageRef);
  UIGraphicsEndImageContext();

Here you go - the map is in black and white colors. To do this dynamically to move monochrome map along with user's gesture you can subclass gesture recognizer, add it directly to map view. Then on every touchesMoved call you can call this redraw function while reusing context you once created. I tried this with apple map taking about 1/3 of the screen and it resulted in extremely low performance (about 3 FPS on iPhone 5s) and there are couple bugs you have to fix, such as: flying map on swipe gesture while monochrome image is static, frequency of map pins redrawing, redraw or animate every pop up that displayed in response to tap. You can try to render map in CALayer, not in Context, Apple recommends it to improve performance, but considering all the above I suppose if you want to see dynamic filtered elements it is better to use other more customizable map: GoogleMap or MapBox.

Rune answered 18/10, 2017 at 7:55 Comment(0)
T
2

To keep it simple, you can add a layer with color blend filter. First force the VC to dark mode:

    override func viewDidLoad() {
        super.viewDidLoad()
        overrideUserInterfaceStyle = .dark
    }

Then, add the layer as follows:

        let darkLayer = CALayer()
        darkLayer.frame = self.view.bounds
        darkLayer.compositingFilter = "colorBlendMode"
        darkLayer.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0).cgColor
        self.view.layer.addSublayer(darkLayer)

If you want map to darker, you can add additional layers like this:

        let darkLayer2 = CALayer()
        darkLayer2.frame = self.view.bounds
        darkLayer2.compositingFilter = "overlayBlendMode"
        darkLayer2.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.4).cgColor
        self.view.layer.addSublayer(darkLayer2)

Resulting map looks like this. You will not see any flicker between color and B/W as you pan the map around.

enter image description here

Tezel answered 22/5, 2022 at 17:1 Comment(4)
This also applies the filter to the annotations!Darn
If you add the dark layer to self.subviews[0].subviews[0].layer.addSublayer(darkLayer) it leaves the annotations in their original colorTezel
@sumancherukuri with this solution, also the one in comment above, it also changes the color of a polyline overlay. Do you by any chance have the solution to not blend that color as well?Innkeeper
same question here, the annotation colour will be dark too. Can we still have colorful annotation?Crape

© 2022 - 2024 — McMap. All rights reserved.