Question

I'm writing an application for iOS 7.0+ and I wanted to use new feature witch is: imageWithRenderingMode. I have a map annotation with this code:

-(MKAnnotationView*)annotationView {
    MKAnnotationView *annotationView = [[MKAnnotationView alloc] initWithAnnotation:self reuseIdentifier:@"AnnotationIdentifier"];
    annotationView.enabled = YES;
    annotationView.canShowCallout = YES;
    annotationView.image = [[UIImage imageNamed:@"star"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
    annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    annotationView.tintColor = [UIColor redColor];
    return annotationView;
}

I was expected that my pins will be red, but stays black, only the callout (i) icon turns red.

enter image description here

I was trying to set self.view.tintColor and self.mapView.tintColor in my ViewController but this not working either. How to turns this pins red?

Was it helpful?

Solution

There's a better solution than those proposed that doesn't involve creating a UIImageView.

This Swift code will create a colored version of your UIImage.

extension UIImage {

    func colorized(color : UIColor) -> UIImage {
        let rect = CGRectMake(0, 0, self.size.width, self.size.height);
        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0);
        let context = UIGraphicsGetCurrentContext();
        CGContextSetBlendMode(context, .Multiply)
        CGContextDrawImage(context, rect, self.CGImage)
        CGContextClipToMask(context, rect, self.CGImage)
        CGContextSetFillColorWithColor(context, color.CGColor)
        CGContextFillRect(context, rect)
        let colorizedImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return colorizedImage
    }
}

Call it like this:

UIImage(named: "myImage")!
    .imageWithRenderingMode(.AlwaysTemplate)
    .colorized(UIColor.red())

OTHER TIPS

I could make this work by capturing an UIView to UIImage:

UIImage *pin = [UIImage imageNamed:@"pin"];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, pin.size.width, pin.size.height)];
imageView.image = [pin imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
imageView.tintColor = [UIColor grayColor]; // set the desired color

// now the magic...
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, imageView.opaque, 0.0);
[imageView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

annotationView.image = img;

How frustrating. I solved it like this (sans the other property configurations):

-(MKAnnotationView*)annotationView {
    MKAnnotationView *annotationView = [[MKAnnotationView alloc] initWithAnnotation: self reuseIdentifier: @"AnnotationIdentifier"];
    UIImage *image = [[UIImage imageNamed: @"star"] imageWithRenderingMode: UIImageRenderingModeAlwaysTemplate];
    UIImageView *screwYouApple = [[UIImageView alloc] initWithImage: image];
    screwYouApple.tintColor = [UIColor redColor];
    [annotationView addSubview: screwYouApple];
    return annotationView;
}

Basically, I blow off the image property of the annotationView and use a properly tinted UIImageView as a subview of the annotation.

Variable naming helped the experience be cathartic.

Same as @sandy-chapman but in Swift 3. My images got flipped so I flip them back again.

extension UIImage {

  func colorized(color : UIColor) -> UIImage {

    let rect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)

    UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
    if let context = UIGraphicsGetCurrentContext() {
        context.setBlendMode(.multiply)
        context.translateBy(x: 0, y: self.size.height)
        context.scaleBy(x: 1.0, y: -1.0)
        context.draw(self.cgImage!, in: rect)
        context.clip(to: rect, mask: self.cgImage!)
        context.setFillColor(color.cgColor)
        context.fill(rect)
    }

    let colorizedImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()
    return colorizedImage!

  }
}

this works for me:

extension MKAnnotationView {
    
    public func set(image: UIImage, with color : UIColor) {
        let view = UIImageView(image: image.withRenderingMode(.alwaysTemplate))
        view.tintColor = color
        UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
        guard let graphicsContext = UIGraphicsGetCurrentContext() else { return }
        view.layer.render(in: graphicsContext)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.image = image
    }
        
}

(based on @Marcio Fonseca's answer)


edit: here's a small tweak to have both full color and tinted images:

extension MKAnnotationView {

    public func setPin(image: UIImage = UIImage(systemName: "mappin.circle.fill")!,
                       with color : UIColor? = nil) {
        let view: UIImageView
        
        if let color = color {
            // set tint color if specified
            view = UIImageView(image: image.withRenderingMode(.alwaysTemplate))
            view.tintColor = color
        } else {
            // keep original image colors if unspecified
            view = UIImageView(image: image)
        }
        
        UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
        guard let graphicsContext = UIGraphicsGetCurrentContext() else { return }
        view.layer.render(in: graphicsContext)
        
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.image = image
    }
}

here's how to call it:

// Use fallback pin image ("mappin.circle.fill") without tint
view.setPin()
// note: this will set it to black.
// you could change the default UIColor? value to .systemRed and pass nil in
// the rare case you might need to use an untinted app asset image

// Use fallback pin image with tint
view.setPin(with: .systemYellow)

// App assets image without tint
view.setPin(image: UIImage(named: "estech")!)

// App assets image with tint
view.setPin(image: UIImage(named: "estech")!, with: .systemBlue)

Realize I'm very late to the game, however I was looking for the same thing as you, but found this useful.

Use MKMarkerAnnotationView, which has an attribute markerTintColor.

You won't get a custom pin image, but it will look a lot like the marker you had in your questions (see my attached image).

Here's a snippet of how I use it:

  // Somewhere else 
  mapView.register(MKMarkerAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMarkerAnnotationView.self.description())  

  func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        guard let companyAnnotation = annotation as? CompanyAnnotation else { return nil }
        let view = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: MKMarkerAnnotationView.self.description())
        view.markerTintColor = .colorFor(category: companyAnnotation.company.category)
        return view
   }

enter image description here

It does not appear that you are able to accomplish this at all using tintColor. MKAnnotationView does not use the tint color for image rendering like a UIImageView does. You'll need to either write some sort of method to draw a correctly colored image yourself, or provide an image with the color you want.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top