8

My application has a toolbar with image buttons on them (subclass of UIButton); when the user switches on the "Bold text" accessibility option, not only the text becomes bold but the images follow suit.

This is the toolbar in normal mode:

Toolbar in normal mode

When the "bold text" is enabled:

Toolbar in bold mode

It seems to be caused by my UIButton subclass, which is included below. I'm using this class to apply an image tint colour when the button is clicked, disabled, etc. and prevents having to include multiple states of each button. For that I'm using the UIImageRenderingModeAlwaysTemplate which reportedly exhibits this observed behaviour.

I've tried to uncheck the "Accessibility" option in the interface builder, but that had no effect at all. Is there a way to fix this?

#import "AppButton.h"

@implementation AppButton

- (id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super initWithCoder:aDecoder]) {
        [self initialize];
    }
    return self;
}

- (void)initialize
{
    self.adjustsImageWhenHighlighted = NO;

    [self setImage:[[self imageForState:UIControlStateNormal] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal];
}

- (void)updateButtonView
{
    if (!self.enabled) {
        self.imageView.tintColor = [UIColor colorWithRGBValue:RGBValueC9];
    } else if (self.highlighted) {
        self.imageView.tintColor = self.highlightTintColor;
    } else {
        self.imageView.tintColor = self.tintColor;
    }
}

- (void)setHighlighted:(BOOL)highlighted
{
    [super setHighlighted:highlighted];
    [self updateButtonView];
}

- (void)setEnabled:(BOOL)enabled
{
    [super setEnabled:enabled];
    [self updateButtonView];
}

- (void)setTintColor:(UIColor *)tintColor
{
    [super setTintColor:tintColor];
    [self updateButtonView];
}

@end
Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
  • Can u refer @Kirby Todd answer for applying Tint Color instead of Default one you are using with RenderingModeAlwaysTemplate.. http://stackoverflow.com/questions/19829356/color-tint-uibutton-image – Vidhyanand May 14 '15 at 04:55
  • What a weird feature! Just ran into this issue in my app. Is this documented anywhere!? – Max Aug 29 '18 at 20:51

4 Answers4

4

I recommend you to use a custom category to tint your image buttons. Here is a simple implementation that does just that:

UIImage+TintImage.h

@interface UIImage (TintImage)
- (UIImage *)imageTintedWithColor:(UIColor *)tintColor;
@end

UIImage+TintImage.m

#import "UIImage+TintImage.h"

@implementation UIImage (TintImage)
- (UIImage *)imageTintedWithColor:(UIColor *)tintColor
{
    if (tintColor == nil) {
        tintColor = [UIColor whiteColor];
    }

    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f);

    // Tint image
    [tintColor set];
    UIRectFill(rect);
    [self drawInRect:rect blendMode:kCGBlendModeDestinationIn alpha:1.0f];
    UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return tintedImage;
}
@end

To use it, just import "UIImage+TintImage.h", then do the following:

UIImage *originalImage = [UIImage imageNamed:@"icn-menu"];
UIImage *tintedImage = [originalImage imageTintedWithColor:[UIColor blueColor]];
UIButton *homeButton = [UIButton buttonWithType:UIButtonTypeCustom];
[homeButton setImage:originalImage forState:UIControlStateNormal];
[homeButton setImage:tintedImage forState:UIControlStateHighlighted];

button showcase

Rufel
  • 2,630
  • 17
  • 15
  • @Jack did you have time to try it, or did you solve your problem? – Rufel May 19 '15 at 17:51
  • Thanks! That helped me enough to fix my issue quite easily; the bonus is yours :) – Ja͢ck May 20 '15 at 05:28
  • what is the solution in swift plz? my code is : `var image = UIImage(named: "home") as UIImage? image = image?.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)` – Hassan Taleb Sep 28 '15 at 13:38
1

Swift solution:

Set Default rendering mode on image (in asset catalog or from the code).

Transform the image color with this function:

extension UIImage {
    public func transform(withNewColor color: UIColor) -> UIImage
    {
        UIGraphicsBeginImageContextWithOptions(size, false, scale)

        let context = UIGraphicsGetCurrentContext()!
        context.translateBy(x: 0, y: size.height)
        context.scaleBy(x: 1.0, y: -1.0)
        context.setBlendMode(.normal)

        let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
        context.clip(to: rect, mask: cgImage!)

        color.setFill()
        context.fill(rect)

        let newImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return newImage
    }

}

Set transformed image to button desired state:

let redImage = image.transform(withNewColor: UIColor.red)
btn?.setImage(redImage, for: .selected)
Krešimir Prcela
  • 4,257
  • 33
  • 46
0

Thanks to Rufel's answer I was able to fix my issue and reduce the code of my class at the same time:

#import "AppButton.h"

@interface AppButton ()

@property (readonly) UIImage *normalImage;

@end

@implementation AppButton

@synthesize highlightTintColor = _highlightTintColor;

- (id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super initWithCoder:aDecoder]) {
        [self initialize];
    }
    return self;
}

- (UIImage *)normalImage
{
    return [self imageForState:UIControlStateNormal];
}

- (void)initialize
{
    self.adjustsImageWhenHighlighted = NO;
    // set disabled image
    [self setImage:[self image:self.normalImage tintedWithColor:[UIColor colorWithRGBValue:RGBValueC9]] forState:UIControlStateDisabled];
}

- (void)setHighlightTintColor:(UIColor *)highlightTintColor
{
    _highlightTintColor = highlightTintColor;
    // update highlighted image
    if (highlightTintColor) {
        [self setImage:[self image:self.normalImage tintedWithColor:highlightTintColor] forState:UIControlStateHighlighted];
    }
}

- (UIImage *)image:(UIImage *)image tintedWithColor:(UIColor *)tintColor
{
    CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
    UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0f);

    // Tint image
    [tintColor set];
    UIRectFill(rect);
    [image drawInRect:rect blendMode:kCGBlendModeDestinationIn alpha:1.0f];
    UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return tintedImage;
}

@end
Community
  • 1
  • 1
Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
0

This is a swift 3 version of rufel Answer ,

extension UIImageView {
fileprivate func tintImage(color: UIColor){
    guard  let _image = image else { return }
    let rect  = CGRect(x: 0.0, y: 0.0, width: _image.size.width  , height: _image.size.height  )
    UIGraphicsBeginImageContextWithOptions(_image.size , false, _image.scale)
    color.set()
    UIRectFill(rect)
    _image.draw(in: rect, blendMode: CGBlendMode.destinationIn, alpha: 1.0)
    image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
}
}

OR

extension UIImage {
static func imageTinted(image: UIImage?, color: UIColor) -> UIImage? {
    let rect  = CGRect(x: 0.0, y: 0.0, width: image?.size.width ?? 0.0, height: image?.size.height ?? 0.0)
    UIGraphicsBeginImageContextWithOptions(image?.size ?? CGSize.zero, false, image?.scale ?? 2.0)
    color.set()
    UIRectFill(rect)
    image?.draw(in: rect, blendMode: CGBlendMode.destinationIn, alpha: 1.0)
    let tinted = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext();
    return tinted;
}
}
M.Othman
  • 5,132
  • 3
  • 35
  • 39