Change only one specific UITabBarItem tint color - ios

It is well known that the tint color of selected (or active) items in a UITabBarController can be easily changed, here is an example:
myBarController.tabBar.tintColor = [UIColor redColor];
In this instance, any tab bar item in tabBar will have a red tint once it is made active. Again, this applies to all of the items in this tab bar.
How can the active tint color be different between other tab bar items in the same bar? For example, one item might have a red tint while selected, while another might have a blue tint.
I am aware that this can probably be solved by redrawing and subclassing the entire tab bar. However, this is the only change I need, and it seems overkill to do so. I'm not trying to change the style or how the items are rendered in any way, just to make that style different between different items.
I haven't seen any answers to this question anywhere that are relevant to the updates in iOS 7 and 8.

There is a much easier way to do this!
Add this to the ViewController which UITabBar Item should be in another color
- (void) viewWillAppear:(BOOL)animated {
// change tint color to red
[self.tabBarController.tabBar setTintColor:[UIColor redColor]];
[super viewWillAppear: animated];
}
Insert this to the other ViewControllers
- (void) viewWillAppear:(BOOL)animated {
// change tint color to black
[self.tabBarController.tabBar setTintColor:[UIColor blackColor]];
[super viewWillAppear: animated];
}
I use this to get different Tint colors in each ViewController
e.g.: [ red | black | green | pink ]

#element119 solution using swift(for you lazy guys):
extension UIImage {
func tabBarImageWithCustomTint(tintColor: UIColor) -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
let context: CGContextRef = UIGraphicsGetCurrentContext()
CGContextTranslateCTM(context, 0, self.size.height)
CGContextScaleCTM(context, 1.0, -1.0)
CGContextSetBlendMode(context, kCGBlendModeNormal)
let rect: CGRect = CGRectMake(0, 0, self.size.width, self.size.height)
CGContextClipToMask(context, rect, self.CGImage)
tintColor.setFill()
CGContextFillRect(context, rect)
var newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
newImage = newImage.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
return newImage
}
}
I'm using this code to tint my middle icon red:
if let items = self.tabBar.items as? [UITabBarItem] {
let button = items[1]
button.image = button.image?.tabBarImageWithCustomTint(UIColor.redColor())
}

I did some experimenting and based on this answer, found a way to do what I want without subclassing UITabBarItem or UITabBar!
Basically, the idea is to create a method of UIImage that mimics the tint mask behavior of UITabBar, while rendering it in its "original" form and avoiding the native tint mask.
All you have to do is create a new instance method of UIImage that returns an image masked with the color we want:
#interface UIImage(Overlay)
- (instancetype)tabBarImageWithCustomTint:(UIColor *)tintColor;
#end
#implementation UIImage(Overlay)
- (instancetype)tabBarImageWithCustomTint:(UIColor *)tintColor
{
UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0, self.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
CGContextClipToMask(context, rect, self.CGImage);
[tintColor setFill];
CGContextFillRect(context, rect);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
newImage = [newImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
return newImage;
}
#end
This is a fairly straightforward version of the code in the answer that I posted, with one exception- the returned image has its rendering mode set to always original, which makes sure that the default UITabBar mask won't be applied. Now, all that is needed is to use this method when editing the tab bar item:
navController.tabBarItem = [[UITabBarItem alloc] initWithTitle:#"title" image:normal_image selectedImage:[selected_image tabBarImageWithCustomTint:[UIColor redColor]]];
Needless to say, selected_image is the normal image one gets from UIImage imageNamed: and the [UIColor redColor can be replaced with any color one desires.

This worked fine for me!! code for Swift 3
extension UIImage {
func tabBarImageWithCustomTint(tintColor: UIColor) -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
let context: CGContext = UIGraphicsGetCurrentContext()!
context.translateBy(x: 0, y: self.size.height)
context.scaleBy(x: 1.0, y: -1.0)
context.setBlendMode(CGBlendMode(rawValue: 1)!)
let rect: CGRect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)
context.clip(to: rect, mask: self.cgImage!)
tintColor.setFill()
context.fill(rect)
var newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
newImage = newImage.withRenderingMode(UIImageRenderingMode.alwaysOriginal)
return newImage
}
}
and after...
button.image = button.image?.tabBarImageWithCustomTint(tintColor: UIColor(red: 30.0/255.0, green: 33.0/255.0, blue: 108.0/255.0, alpha: 1.0))
thanks ;))

swift xcode7.1 tested :
extension UIImage {
func tabBarImageWithCustomTint(tintColor: UIColor) -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
let context: CGContextRef = UIGraphicsGetCurrentContext()!
CGContextTranslateCTM(context, 0, self.size.height)
CGContextScaleCTM(context, 1.0, -1.0)
CGContextSetBlendMode(context, CGBlendMode.Normal)
let rect: CGRect = CGRectMake(0, 0, self.size.width, self.size.height)
CGContextClipToMask(context, rect, self.CGImage)
tintColor.setFill()
CGContextFillRect(context, rect)
var newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
newImage = newImage.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
return newImage
}
}
fixed the compatibility bug in #Binsh answer

Swift 5:
extension UIImage {
func tintWithColor(color: UIColor) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
guard let context = UIGraphicsGetCurrentContext() else { return nil }
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: 0.0, y: -self.size.height)
context.setBlendMode(.multiply)
let rect = CGRect(origin: .zero, size: size)
guard let cgImage = self.cgImage else { return nil }
context.clip(to: rect, mask: cgImage)
color.setFill()
context.fill(rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}

Related

Fill UIImage with UIColor

I have a UIImage that I want to fill with UIColor
I've tried this code but the app crashes on the 10th row.
Here's the code:
extension UIImage {
func imageWithColor(_ color: UIColor) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, false, scale)
let context = UIGraphicsGetCurrentContext()
context?.translateBy(x: 0.0, y: size.height)
context?.scaleBy(x: 1.0, y: -1.0)
context?.setBlendMode(CGBlendMode.normal)
let rect = CGRect(origin: CGPoint.zero, size: size)
context?.clip(to: rect, mask: context as! CGImage)// crashes
color.setFill()
context?.fill(rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
return newImage!
}
}
The problem is probably on context?.clip(to: rect, mask: context as! CGImage) (I think I shouldn't send context as the mask, but what should I send? Both CGImage() and CGImage.self don't work.
You have to do as follow:
extension UIImage {
func tinted(with color: UIColor) -> UIImage? {
defer { UIGraphicsEndImageContext() }
UIGraphicsBeginImageContextWithOptions(size, false, scale)
color.set()
withRenderingMode(.alwaysTemplate).draw(in: CGRect(origin: .zero, size: size))
return UIGraphicsGetImageFromCurrentImageContext()
}
}
You need to end the image context when you finish drawing:
UIGraphicsEndImageContext();
Or you could add a category method for UIImage:
- (UIImage *)imageByTintColor:(UIColor *)color
{
UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
[color set];
UIRectFill(rect);
[self drawAtPoint:CGPointMake(0, 0) blendMode:kCGBlendModeDestinationIn alpha:1];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Using as:
image = [image imageByTintColor:color];
Simplest way to doing this
theImageView.image? = (theImageView.image?.imageWithRenderingMode(.AlwaysTemplate))!
theImageView.tintColor = UIColor.magentaColor()
let image = UIImage(named: "whatever.png")?.imageWithRenderingMode(.alwaysTemplate)
When you set this image later to UIButton or UIImageView - just change tint color of that control, and image will be drawn using tint color you specified.

How to create an overlay color with a gradient in iOS

Background info: I have a UIImageView. I have added an overlay colour on its image in the following way:
UIGraphicsBeginImageContext(initialImage.size);
[initialImage drawInRect:CGRectMake(0, 0, initialImage.size.width, initialImage.size.height) blendMode:kCGBlendModeNormal alpha:alphaValue];
UIBezierPath * path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, initialImage.size.width, initialImage.size.height)];
[overlayColor setFill];
[path fillWithBlendMode:kCGBlendModeMultiply alpha:1];
finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self setImage:finalImage];
I still want to add this as an overlay colour but I want it to have a gradient. I have been trying to figure out a way to do this but haven't really been successful. I guess, the approach of adding an overlay colour with a gradient is wrong? I'm not sure about how to do this. I have tried to add a CGGradientLayer as a sublayer to the UIImageView but it doesn't work.
I thought about adding a UIView and setting it's backgroundColor as the overlayColorand then adding a CGGradientLayer as sublayer of the UIView which is added as a subview to the UIImageView but, we are not supposed to add subviews to UIImageViews.
Can someone please help me with this? Maybe I should change my approach?
Pointing me in the right direction will be great as well!
I look forward to your responses and apologies if this post hasn't been entirely clear!
Thanks in advance for your help!
Edit: Code for the CGGradientLayer
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = self.frame;
UIColor *colorOne = [UIColor colorFromHex:self.feedColor withAlpha:(alphaValue * 0.7)];
UIColor *colorTwo = [UIColor colorFromHex:self.feedColor withAlpha:(alphaValue * 1.0)];
gradient.colors = [NSArray arrayWithObjects:(id)colorOne.CGColor, (id)colorTwo.CGColor, nil];
[self.layer insertSublayer:gradient atIndex:0];
I would just use Core Graphics in order to take your input image, apply a gradient overlay to it, and then pass it onto the UIImageView. Something like this should achieve the desired result:
- (UIImage *)imageWithGradientOverlay:(UIImage *)sourceImage color1:(UIColor *)color1 color2:(UIColor *)color2 gradPointA:(CGPoint)pointA gradPointB:(CGPoint)pointB {
CGSize size = sourceImage.size;
// Start context
UIGraphicsBeginImageContext(size);
CGContextRef c = UIGraphicsGetCurrentContext();
// Draw source image into context
CGContextDrawImage(c, (CGRect){CGPointZero, size}, sourceImage.CGImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat gradLocs[] = {0, 1};
NSArray *colors = #[(id)color1.CGColor, (id)color2.CGColor];
// Create a simple linear gradient with the colors provided.
CGGradientRef grad = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, gradLocs);
CGColorSpaceRelease(colorSpace);
// Draw gradient with multiply blend mode over the source image
CGContextSetBlendMode(c, kCGBlendModeMultiply);
CGContextDrawLinearGradient(c, grad, pointA, pointB, 0);
CGGradientRelease(grad);
// Grab resulting image from context
UIImage *resultImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImg;
}
This is where sourceImage is the input image, color1 and color2 are your gradient colors and gradPointA and gradPointB are your linear gradient end points (In Core Graphics coordinate system, bottom left is (0,0)).
This way you save having to mess about with layers. If you're frequently re-drawing with different colors, then you may want to take an approach that uses layers.
If you're looking for a more dynamic approach, then I would subclass CALayer instead of UIImageView. Therefore you'd want something like this:
#interface gradientImageLayer : CALayer
- (instancetype)initWithFrame:(CGRect)frame;
#end
#implementation gradientImageLayer
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super init]) {
self.opaque = YES; // Best for performance, but you if you want the layer to have transparency, then remove.
UIImage *i = [UIImage imageNamed:#"foo2.png"]; // Replace with your image
self.frame = frame;
self.contentsScale = [UIScreen mainScreen].nativeScale;
self.contents = (__bridge id _Nullable)(i.CGImage);
// Your code for the CAGradientLayer was indeed correct.
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = frame;
// Add whatever colors you want here.
UIColor *colorOne = [UIColor colorWithRed:1 green:1 blue:0 alpha:0.1];
UIColor *colorTwo = [UIColor colorWithRed:0 green:1 blue:1 alpha:0.2];
gradient.colors = #[(id)colorOne.CGColor, (id)colorTwo.CGColor]; // Literals read far nicer than a clunky [NSArray arrayWith.... ]
[self addSublayer:gradient];
}
return self;
}
#end
The downside to this approach is you are unable to apply different blend modes. The only solutions I've seen to applying a blend mode on a CALayer is through Core Graphics, but then you'd be better off with my original answer.
I'll post the collaborative solution in this question
This category allows you to add a Color Overlay or Gradient Color Overlay using any blend mode (multiply, normal, etc) and CoreGraphics
Swift 4:
extension UIImage {
//creates a static image with a color of the requested size
static func fromColor(color: UIColor, size: CGSize) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
context?.setFillColor(color.cgColor)
context?.fill(rect)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img!
}
func blendWithColorAndRect(blendMode: CGBlendMode, color: UIColor, rect: CGRect) -> UIImage {
let imageColor = UIImage.fromColor(color: color, size:self.size)
let rectImage = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)
UIGraphicsBeginImageContextWithOptions(self.size, true, 0)
let context = UIGraphicsGetCurrentContext()
// fill the background with white so that translucent colors get lighter
context!.setFillColor(UIColor.white.cgColor)
context!.fill(rectImage)
self.draw(in: rectImage, blendMode: .normal, alpha: 1)
imageColor.draw(in: rect, blendMode: blendMode, alpha: 0.8)
// grab the finished image and return it
let result = UIGraphicsGetImageFromCurrentImageContext()
//self.backgroundImageView.image = result
UIGraphicsEndImageContext()
return result!
}
//creates a static image with a gradient of colors of the requested size
static func fromGradient(colors: [UIColor], locations: [CGFloat], horizontal: Bool, size: CGSize) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
let colorSpace = CGColorSpaceCreateDeviceRGB()
let cgColors = colors.map {$0.cgColor} as CFArray
let grad = CGGradient(colorsSpace: colorSpace, colors: cgColors , locations: locations)
let startPoint = CGPoint(x: 0, y: 0)
let endPoint = horizontal ? CGPoint(x: size.width, y: 0) : CGPoint(x: 0, y: size.height)
context?.drawLinearGradient(grad!, start: startPoint, end: endPoint, options: .drawsAfterEndLocation)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img!
}
func blendWithGradientAndRect(blendMode: CGBlendMode, colors: [UIColor], locations: [CGFloat], horizontal: Bool = false, alpha: CGFloat = 1.0, rect: CGRect) -> UIImage {
let imageColor = UIImage.fromGradient(colors: colors, locations: locations, horizontal: horizontal, size: size)
let rectImage = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)
UIGraphicsBeginImageContextWithOptions(self.size, true, 0)
let context = UIGraphicsGetCurrentContext()
// fill the background with white so that translucent colors get lighter
context!.setFillColor(UIColor.white.cgColor)
context!.fill(rectImage)
self.draw(in: rectImage, blendMode: .normal, alpha: 1)
imageColor.draw(in: rect, blendMode: blendMode, alpha: alpha)
// grab the finished image and return it
let result = UIGraphicsGetImageFromCurrentImageContext()
//self.backgroundImageView.image = result
UIGraphicsEndImageContext()
return result!
}
}
Example Gradient:
let newImage = image.blendWithGradientAndRect(blendMode: .multiply,
colors: [.red, .white],
locations: [0, 1],
horizontal: true,
alpha: 0.8,
rect: imageRect)
Example Single Color:
let newImage = image.blendWithColorAndRect(blendMode: .multiply, color: .red, rect: imageRect)

Transparent UIImage is not tranrperent in navBar

I've created an white image with a white alpha on 0.2. However when i add it it does not seem to be transparent in the navigationBar. It is completely white. i did the same thing with tabBar, which worked fine. What am i doing wrong. this is in a viewControllers ViewDidLoad:
self.navigationController?.navigationBar.setBackgroundImage(UIImage(named: "Navbar"), forBarMetrics: UIBarMetrics.Default)
One foolproof way of controlling the alpha value of a UIImage when using UIImageView is not an option is giving the image an initial alpha of 1, then adding this UIImage extension:
extension UIImage{
func alpha(value:CGFloat)->UIImage
{
UIGraphicsBeginImageContextWithOptions(self.size, false, 0.0)
let ctx = UIGraphicsGetCurrentContext();
let area = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -area.size.height);
CGContextSetAlpha(ctx, value);
CGContextDrawImage(ctx, area, self.CGImage);
let newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
}
...then in ViewDidLoad:
let newNavBar = UIImage(named: "navBar")!.alpha(0.2)
self.navigationController?.navigationBar.setBackgroundImage(UIImage(named: "newNavBar"), forBarMetrics: UIBarMetrics.Default)

Tint is inverting colors in swift

I'm trying out this code to tint color a UIImage but it seems to be inverting the colors - coloring the background and turning the black stroke to white.
How do I have it color the black stroke lines of an image and leave the white background alone? Here's a playground sample where I try one technique as well as another one suggested by the first answer.
Playground Code
extension UIImage {
func tint(color: UIColor, blendMode: CGBlendMode = CGBlendMode.Normal) -> UIImage
{
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale);
let context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0, self.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetBlendMode(context, blendMode);
let rect = CGRectMake(0, 0, self.size.width, self.size.height);
CGContextClipToMask(context, rect, self.CGImage);
color.setFill()
CGContextFillRect(context, rect);
let newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
func tint2(color: UIColor, blendMode: CGBlendMode) -> UIImage
{
let drawRect = CGRectMake(0.0, 0.0, size.width, size.height)
UIGraphicsBeginImageContextWithOptions(size, false, scale)
let context = UIGraphicsGetCurrentContext()
CGContextClipToMask(context, drawRect, CGImage)
color.setFill()
UIRectFill(drawRect)
drawInRect(drawRect, blendMode: blendMode, alpha: 1.0)
let tintedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return tintedImage
}
}
let stringUrl = "https://s3.amazonaws.com/neighbor-chat-assets/icons/avatar1000.png"
let url = NSURL(string: stringUrl)
let data = NSData(contentsOfURL: url!)
var originalimage = UIImage(data: data!)
var image = originalimage!.imageWithRenderingMode(.AlwaysTemplate).tint(UIColor.blueColor(), blendMode:.Normal)
var image2 = originalimage!.imageWithRenderingMode(.AlwaysTemplate).tint2(UIColor.blueColor(), blendMode:.Normal)
This should work, I just tested it with a png and transparent background
func tint(color: UIColor, blendMode: CGBlendMode = CGBlendMode.Normal) -> UIImage
{
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale);
let context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0, self.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetBlendMode(context, blendMode);
let rect = CGRectMake(0, 0, self.size.width, self.size.height);
CGContextClipToMask(context, rect, self.CGImage);
color.setFill()
CGContextFillRect(context, rect);
let newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Also see this post for further information. The alternative would be to wrap your image in a UIImageView with UIImageRenderingMode.AlwaysTemplate rendering mode, and then set the tintColor property.
EDIT
So this is the function you can use to first mask out certain colors if you don't have an alpha channel in an image
func tint(color: UIColor, blendMode: CGBlendMode = CGBlendMode.Normal, colorToMask: UIColor = UIColor.blackColor()) -> UIImage
{
var fRed : CGFloat = 0
var fGreen : CGFloat = 0
var fBlue : CGFloat = 0
var fAlpha: CGFloat = 0
colorToMask.getRed(&fRed, green: &fGreen, blue: &fBlue, alpha: &fAlpha)
let rect = CGRectMake(0, 0, self.size.width, self.size.height);
var colorMasking : [CGFloat] = [fRed,fRed,fGreen,fGreen,fBlue,fBlue]
let newImageRef = CGImageCreateWithMaskingColors(self.CGImage!, &colorMasking)
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale);
let context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0, self.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetBlendMode(context, blendMode);
CGContextClipToMask(context, rect, newImageRef!);
color.setFill()
CGContextFillRect(context, rect);
let newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
This function first creates a mask depending on the specified color, and clips the context to this mask.
EDIT #2
As stated in the comments, I also didn't manage to mask out the white color, I tried UInt8 values (i.e. 255) and float values (i.e. 1.0), but it still didn't work. So the only solution I could come up with was to invert the image. Here is the function:
func negativeImage() -> UIImage {
let coreImage = MYImage(CGImage: self.CGImage!)
let filter = CIFilter(name: "CIColorInvert", withInputParameters: ["inputImage" : coreImage])!
let result = filter.outputImage!
let context = CIContext(options:nil)
let cgimg : CGImageRef = context.createCGImage(result, fromRect: result.extent)
let bmpcontext = CGBitmapContextCreate(nil, Int(self.size.width), Int(self.size.height), 8, Int(self.size.width)*4, CGColorSpaceCreateDeviceRGB(),CGImageAlphaInfo.NoneSkipLast.rawValue)
CGContextDrawImage(bmpcontext, CGRectMake(0, 0, self.size.width, self.size.height), cgimg)
let newImgRef = CGBitmapContextCreateImage(bmpcontext)!
return UIImage(CGImage: newImgRef)
}
You can use it like this
var image : UIImage = ...
image = image.negativeImage()
image = image.tint(UIColor.redColor())
Here are some general notes
CGImageCreateWithMaskingColors requires a CGImage with NO alpha channel, that's why I use the flag CGImageAlphaInfo.NoneSkipLast in the negativeImage function in order to remove the alpha channel again
There is absolutely no error checking included in this, you should at least check whether there is an alpha channel included within the tint function using CGImageGetAlphaInfo
You can either have the masked out parts (white in our example) transparent (by setting false in the UIGraphicsBeginImageContextWithOptions command within the tint function), or a specific color by setting true, which is black by default (you would need to paint before clipping to the mask).

Change colour of unselected Tab Bar Icon in swift [duplicate]

Environment:
- Xcode 6 beta 4
- Swift language
- iOS Tabbed Application (default xCode project)
How can I change the default grey color of the tabs to something else? (Preferably globally)
As far as my research goes I need to somehow change the image rendering mode for each tab to Original rendering mode however I don't know how
Each (default) tab bar item consists of text and icon. It is pretty easy to change the text colors globally by specifying the appearance:
// you can add this code to you AppDelegate application:didFinishLaunchingWithOptions:
// or add it to viewDidLoad method of your TabBarController class
UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.magentaColor()], forState:.Normal)
UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.redColor()], forState:.Selected)
With images situation is a little bit more complicated. You cannot define their appearance globally. You should redefine them in your TabBarController class. Add code bellow to viewDidLoad method of your TabBarController class:
for item in self.tabBar.items as [UITabBarItem] {
if let image = item.image {
item.image = image.imageWithColor(UIColor.yellowColor()).imageWithRenderingMode(.AlwaysOriginal)
}
}
As we know there is no imageWithColor(...) method in UIImage class. So here is the extension implementation:
// Add anywhere in your app
extension UIImage {
func imageWithColor(tintColor: UIColor) -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
let context = UIGraphicsGetCurrentContext() as CGContextRef
CGContextTranslateCTM(context, 0, self.size.height)
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetBlendMode(context, .Normal)
let rect = CGRectMake(0, 0, self.size.width, self.size.height) as CGRect
CGContextClipToMask(context, rect, self.CGImage)
tintColor.setFill()
CGContextFillRect(context, rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext() as UIImage
UIGraphicsEndImageContext()
return newImage
}
}
imageWithColor was borrowed from this answer: https://stackoverflow.com/a/24545102/3050466
I don't have enough reputation for commenting the comments, but many are interested how to change the color of selected image
just add another if let check after
if let image = item.image
just like this:
if let selectedImage = item.selectedImage {
item.selectedImage = selectedImage.imageWithColor(UIColor.yellowColor()).imageWithRenderingMode(.AlwaysOriginal)
}
this solved the problem perfectly.
And a little addition, since Swift 1.2 and Xcode 6.3.2 you need
for item in self.tabBar.items as! [UITabBarItem]
instead of
for item in self.tabBar.items as [UITabBarItem]
Hope that helps!
Swift 2.0
To change the default color for tab bar images, Add code bellow to viewDidLoad method of your TabBarController class:
for item in self.tabBar.items! as [UITabBarItem] {
if let image = item.image {
item.image = image.imageWithColor(UIColor.yellowColor()).imageWithRenderingMode(.AlwaysOriginal)
}
}
Update the imageWithColor extension. Used with the above method and should be placed outside of your TabBarController class:
extension UIImage {
func imageWithColor(tintColor: UIColor) -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
let context = UIGraphicsGetCurrentContext()! as CGContextRef
CGContextTranslateCTM(context, 0, self.size.height)
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetBlendMode(context, CGBlendMode.Normal)
let rect = CGRectMake(0, 0, self.size.width, self.size.height) as CGRect
CGContextClipToMask(context, rect, self.CGImage)
tintColor.setFill()
CGContextFillRect(context, rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext() as UIImage
UIGraphicsEndImageContext()
return newImage
}
}
No changes to the way text gets coloured but just for reference. Also should be added the code bellow to viewDidLoad:
// you can add this code to you AppDelegate application:didFinishLaunchingWithOptions:
// or add it to viewDidLoad method of your TabBarController class
UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.magentaColor()], forState:.Normal)
UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.redColor()], forState:.Selected)
Swift 3.0
To change the default color for tab bar images, Add code bellow to viewDidLoad method of your TabBarController class:
for item in self.tabBar.items! as [UITabBarItem] {
if let image = item.image {
item.image = image.imageWithColor(tintColor: UIColor.yellow).withRenderingMode(.alwaysOriginal)
}
}
Update the imageWithColor extension. Used with the above method and should be placed outside of your TabBarController class:
extension UIImage {
func imageWithColor(tintColor: UIColor) -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
let context = UIGraphicsGetCurrentContext()! as CGContext
context.translateBy(x: 0, y: self.size.height)
context.scaleBy(x: 1.0, y: -1.0);
context.setBlendMode(CGBlendMode.normal)
let rect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)
context.clip(to: rect, mask: self.cgImage!)
tintColor.setFill()
context.fill(rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()! as UIImage
UIGraphicsEndImageContext()
return newImage
}
}

Resources