104

Is it possible to modify a UIImage's renderingMode from a storyboard or xib editor?

The goal is to apply tintColor to the particular UIImageView object.

Artem Stepanenko
  • 3,423
  • 6
  • 29
  • 51

16 Answers16

211

You can set the image rendering mode not in the .xib file, but in an .xcassets library.

After adding an image to an asset library, select the image and open the attributes inspector on the right side of Xcode. Find the attribute 'Render As' and set it to 'template'.

After setting an image's rendering mode, you can add a tint color to the UIImageView in a .xib or .storyboard file to adjust the image color.

This sets the property on the image wherever it's used rather than just in one interface builder file, but in almost all cases (that I've encountered) this is the behavior you want.

Screenshot of Xcode showing attributes inspector for an image

A few things to note:

  • The image color will not appear to have changed in interface builder (as of Xcode 6.1.1) but will work when the application is run.
  • I've experienced some bugginess with this feature and in some situations I've had to remove and re-add the UIImageView. I have not looked into that deeply.
  • This also works great on other UIKitComponents such as images in UIButton's and UIBarButtonItem's.
  • If you have a bunch of white images that are invisible in your asset library, making them black/transparent images and changing the rendering mode will make your life up to 10x better.
Anthony Mattox
  • 7,048
  • 6
  • 43
  • 59
  • 15
    There seem to be a bug with tint color on `UIImageView`, so the tint color is not always shown when running the app. – Fogh Jan 20 '15 at 08:49
  • @Fogh I've had some inconsistent bugs like that as well. Does setting the tint color programmatically fix this? – Anthony Mattox Jan 20 '15 at 14:12
  • That's unfortunate. Setting tint colors programmatically does have some advantages though as you can then abstract the color references and logic. It may also be worth trying assigning the tint color on a `UIImageView`'s superview to see if that is more reliable. – Anthony Mattox Jan 20 '15 at 14:37
  • 2
    I can confirm that there is an issue when setting the tint color in IB. Setting it programmatically works. I filed a bug #20305518 for it. – Nikolay Derkach Mar 26 '15 at 06:41
  • Note that for the `tint` to work, the image file has to be black and transparent (not b&w) – Axel Guilmin Feb 25 '16 at 16:00
  • 1
    @AxelGuilmin Yup. Actually only the alpha channel is used, so it could be any color as long as the transparency is what you want. – Anthony Mattox Feb 25 '16 at 16:22
  • 1
    It works now with Xcode 7.3 (7D175)! Although the accepted answer is a very intuitive workaround, I prefer this answer instead. – nstein Mar 28 '16 at 08:04
  • This should be the selected answer. – Johann Burgess Jan 12 '17 at 09:47
  • Looks like maybe this doesn't work for 1x image assets. – bugloaf Sep 06 '18 at 20:22
  • Got fixed this issue by settings runtime attribute 'tintColor' in interface builder – Sunil Targe Feb 25 '19 at 08:33
103

Here's how you can do it in .xib or storyboard files:

(Obj-C) Create a category on UIImageView:

@interface UIImageView (Utils)

- (void)setImageRenderingMode:(UIImageRenderingMode)renderMode;

@end

@implementation UIImageView (Utils)

- (void)setImageRenderingMode:(UIImageRenderingMode)renderMode
{
    NSAssert(self.image, @"Image must be set before setting rendering mode");
    self.image = [self.image imageWithRenderingMode:renderMode];
}

@end

(Swift 4) Create an extension for UIImageView:

extension UIImageView {
    func setImageRenderingMode(_ renderMode: UIImage.RenderingMode) {
        assert(image != nil, "Image must be set before setting rendering mode")
        // AlwaysOriginal as an example
        image = image?.withRenderingMode(.alwaysOriginal)
    }
}

Then in the Identity Inspector in the xib file, add a runtime attribute:

enter image description here

Bram
  • 2,718
  • 1
  • 22
  • 43
Snowman
  • 31,411
  • 46
  • 180
  • 303
  • 46
    What I love about this answer, is that it is an answer to the question. – algal Sep 24 '14 at 19:00
  • 1
    I would prefer to create an UIImageView subclass rather to do the key value setting. Because, it this easier to set up and we could avoid the number setting to the enumeration value. For example: ` @implementation TemplateRenderingImageView -(id)initWithCoder:(NSCoder *)aDecoder{ self = [super initWithCoder:aDecoder]; if (self) { self.image = [self.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; } return self; } @end` – john fantasy Oct 24 '14 at 02:44
  • 1
    Great answer! The screenshots were helpful. – BigSauce Dec 11 '14 at 08:05
  • how would xib / storyboard know that there is a category on UIImageView and it needs to use that instead of default UIImageView implementation? – Nirav Bhatt Jun 10 '15 at 10:21
  • Use a subclass instead. – datWooWoo Jun 10 '15 at 18:06
  • 1
    Doesn't work with Launch Screen.xib of course because you can't use user defined runtime attributes with it. – Steve Moser Sep 08 '15 at 15:41
  • 3
    hardcoding 2 as a value is bad bad bad.. The use of xcassets catalog is the right answer. – atastrophic Sep 29 '15 at 22:14
  • @tGilani Since iOS 8, of course it's not the right answer. But for iOS 7, it is the only way to actually make `tintColor` work on `UIImageView`s. Instead of **hardcoding** that `2`, I would define my own value (for example a `BOOL` with a value of `YES`) and my `setImageRenderingMode:` method would be `- (void)setImageRenderingModeToTemplate:(BOOL)asTemplate { UIImageRenderingMode rMode = asTemplate ? UIImageRenderingModeAlwaysTemplate : UIImageRenderingModeAutomatic; self.image = [self.image imageWithRenderingMode:rMode]; };` – Alejandro Iván Mar 31 '16 at 16:43
  • Beautiful answer, but it doesn't work for me. I've tried to implement it in Swift, and I was able to set rendering mode in storyboard, but I got this error when tried run: _this class is not key value coding-compliant for the key imageRenderingMode_. – kelin Jul 14 '17 at 21:01
  • 1
    @kelin I know I'm way late but I have the same issue - I had to add the `@IBInspectable` property to the function to fix it – Quinn Sep 08 '19 at 18:21
  • @Quinn, may be late for me, but never too late for Community. – kelin Sep 08 '19 at 19:46
43

Using the template rendering mode with a UIImageView in a storyboard or xib is very buggy, both on iOS 7 and iOS 8.

On iOS 7

The UIImage is not properly decoded from the storyboard/xib. If you inspect the imageView.image.renderingMode property in the viewDidLoad method, you will notice that it is always UIImageRenderingModeAutomatic, even if you set it to Render As Template Image in your xcassets file.

To workaround, you have to manually set the rendering mode:

self.imageView.image = [self.imageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];

On iOS 8

The UIImage is properly decoded and its renderingMode property reflects what was chosen in the xcassets file but the image is not tinted.

To workaround, you have two options:

  1. Set the tintColor property in the User Defined Runtime Attributes instead of the Attributes inspector pane.

or

  1. Manually reset the tintColor:
UIColor *tintColor = self.imageView.tintColor;
self.imageView.tintColor = nil;
self.imageView.tintColor = tintColor;

You can pick your preferred option, both properly tint the image.

(If you are compiling with Xcode 6.2, just doing self.imageView.tintColor = self.imageView.tintColor; is enough but this doesn’t work anymore if you are compiling with Xcode 6.3)

Conclusion

If you need to support both iOS 7 and iOS 8, you’ll need both workarounds. If you only have to support iOS 8, only one workaround is needed.

0xced
  • 25,219
  • 10
  • 103
  • 255
16

Setting imageView RenderingMode to use the tint color in the storyboard can be reduced to a one-liner:

[self.imageView setImage:[self.imageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]];

Then the image and tint color can all be set in the Storyboard.

ZeMoon
  • 20,054
  • 5
  • 57
  • 98
middleseatman
  • 222
  • 2
  • 5
13

You may fix .xib issues with an extension:

import UIKit

// fixing Bug in XCode
// http://openradar.appspot.com/18448072
extension UIImageView {
    override open func awakeFromNib() {
        super.awakeFromNib()
        self.tintColorDidChange()
    }
}

Source: https://gist.github.com/buechner/3b97000a6570a2bfbc99c005cb010bac

Amazing, this bug has been around for like 4-5 years now.

allaire
  • 5,995
  • 3
  • 41
  • 56
nikans
  • 2,468
  • 1
  • 28
  • 35
8

You cann't set renderingMode either from storyboard or xib. It could access by programmatically.

ex:

UIImage *unSeletedImage = [UIImage imageNamed:@"UnSelected.png"];
selectedImage = [selectedImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
Mani
  • 17,549
  • 13
  • 79
  • 100
  • 3
    I think, you've made small typos in your code. Second line should be `UIImage *selectedImage = [unSeletedImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];` – Artem Stepanenko Oct 22 '13 at 13:23
8

Set tintColor & Class in Storyboard.

//
//  TintColoredImageView.swift
//  TintColoredImageView
//
//  Created by Dmitry Utmanov on 14/07/16.
//  Copyright © 2016 Dmitry Utmanov. All rights reserved.
//

import UIKit

@IBDesignable class TintColoredImageView: UIImageView {

    override var image: UIImage? {
        didSet {
            let _tintColor = self.tintColor
            self.tintColor = nil
            self.tintColor = _tintColor
        }
    }


    override init(frame: CGRect) {
        super.init(frame: frame)
        initialize()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initialize()
    }

    override init(image: UIImage?) {
        super.init(image: image)
        initialize()
    }

    override init(image: UIImage?, highlightedImage: UIImage?) {
        super.init(image: image, highlightedImage: highlightedImage)
        initialize()
    }

    func initialize() {
        let _tintColor = self.tintColor
        self.tintColor = nil
        self.tintColor = _tintColor
    }

}
Dmitrii Cooler
  • 4,032
  • 1
  • 22
  • 21
4

It's very easy to fix

Just create class UIImageViewPDF and use it in your storyboard

IB_DESIGNABLE
@interface UIImageViewPDF : UIImageView

@end

@implementation UIImageViewPDF

- (void) didMoveToSuperview
{
    [super didMoveToSuperview];
    self.image = [self.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
    id color = self.tintColor;
    self.tintColor = color;
}

@end
Igor Popov
  • 41
  • 1
3

Another solution is to create a UIImageView subclass:

final class TemplateImageView: UIImageView {
    override func awakeFromNib() {
        super.awakeFromNib()
        guard let oldImage = image else { return }
        image = nil
        image = oldImage.withRenderingMode(.alwaysTemplate)
    }
}

Then just set the class in the Interface Builder to TemplateImageView.

Rudolf Adamkovič
  • 31,030
  • 13
  • 103
  • 118
2

Simple way to be set from Storyboard:

@IBDesignable
public class CustomImageView: UIImageView {
    @IBInspectable var alwaysTemplate: Bool = false {
        didSet {
            if alwaysTemplate {
                self.image = self.image?.withRenderingMode(.alwaysTemplate)
            } else {
                self.image = self.image?.withRenderingMode(.alwaysOriginal)
            }

        }
    }
}

Works fine on iOS 10 and Swift 3

Rodrigo
  • 553
  • 1
  • 5
  • 17
2

I got fixed this issue by adding runtime attribute tintColor in interface builder.

NOTE : You will still need to set your image to be rendered as a template image in your Images.xcassets file.

enter image description here

Sunil Targe
  • 7,251
  • 5
  • 49
  • 80
1

In iOS 9 setting the tintColor property in Interface Builder is still buggy.

Note that a working solution besides writing lines directly modifying ImageView properties is to set Render As: Template Image in the asset catalog, and call e.g.:

[[UIImageView appearanceWhenContainedInInstancesOfClasses:@[[MyView class]]] setTintColor:[UIColor whiteColor]];
yarp
  • 47
  • 6
0

If you create an IBOutlet you can change it in your awakeFromNib method like so...

self.myImageView.image = [self.myImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];

While @Moby's answer is more correct - this might be more succinct.

amergin
  • 3,106
  • 32
  • 44
0

Crazy this bug is still in iOS 12.1! For storyboards/xibs: Adding a tag to the UIImageView can be a quick fix.

Swift 4.2

view.viewWithTag(1)?.tintColorDidChange()
Jayden Irwin
  • 921
  • 9
  • 14
0
extension UIImageView {

   @IBInspectable var renderModeTemplate : Bool {
       get{
           return image?.renderingMode == .alwaysTemplate
       }

       set{
          image = image?.withRenderingMode(newValue ? .alwaysTemplate:.alwaysOriginal)
       }
   }
}

In storyboard select UIImageView and select inspector, set property renderModeTemplate = On In Storyboard

Ks 2000
  • 1
  • 2
0

As Rudolf also mentioned above, I would define a simple class, like this:

import UIKit

@IBDesignable class TintImage: UIImageView{
    override func layoutSubviews() {
        super.layoutSubviews()
 
        image = image?.withRenderingMode(.alwaysTemplate)
    }
}

After this definition, just add an Image View to storyboard and select its custom class as TintImage. This will activate the "Tint" selection in the storyboard.

JustADeveloper
  • 193
  • 1
  • 9