iOS : How to add Underline in UITabBarItem - ios

I am working in a application where i need to add underline in UITabbarItem.
so i would like to add underline under the selected UITabbarItem in Default UITabbarcontroller of iOS.
I have already created subclass of UITabbarcontroller but didn't find any way to add line in that.
I want to do something like below image.
If anyone have any idea for this please share here.

Code for Swift 3
In didFininshLaunch call method:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
UITabBar.appearance().selectionIndicatorImage = getImageWithColorPosition(color: UIColor.blue, size: CGSize(width:(self.window?.frame.size.width)!/4,height: 49), lineSize: CGSize(width:(self.window?.frame.size.width)!/4, height:2))
return true
}
Method:
func getImageWithColorPosition(color: UIColor, size: CGSize, lineSize: CGSize) -> UIImage {
let rect = CGRect(x:0, y: 0, width: size.width, height: size.height)
let rectLine = CGRect(x:0, y:size.height-lineSize.height,width: lineSize.width,height: lineSize.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
UIColor.clear.setFill()
UIRectFill(rect)
color.setFill()
UIRectFill(rectLine)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}

Please try this one.
I have used in my application once and hope it will help you too.
This is how I have created Tab bar programmatically:
UITabBarController *tab = [[UITabBarController alloc]init];
ViewController1 *v1 = [[ViewController1 alloc]init];
ViewController1 *v2 = [[ViewController1 alloc]init];
ViewController1 *v3 = [[ViewController1 alloc]init];
ViewController1 *v4 = [[ViewController1 alloc]init];
tab.viewControllers = [NSArray
arrayWithObjects:v1,v2,v3,v4,nil];
[[tab.tabBar.items objectAtIndex:0]setImage:[UIImage imageNamed:#""]];
[[tab.tabBar.items objectAtIndex:1]setImage:[UIImage imageNamed:#""]];
[[tab.tabBar.items objectAtIndex:2]setImage:[UIImage imageNamed:#""]];
[[tab.tabBar.items objectAtIndex:3]setImage:[UIImage imageNamed:#""]];
int divide = 4;
if([UIDevice currentDevice].userInterfaceIdiom ==UIUserInterfaceIdiomPad)
divide =6;
}
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(tab.tabBar.frame.origin.x,tab.tabBar.frame.origin.y, self.view.frame.size.width/divide, 56)];
UIImageView *border = [[UIImageView alloc]initWithFrame:CGRectMake(view.frame.origin.x,view.frame.size.height-6, self.view.frame.size.width/divide, 6)];
border.backgroundColor = “your color”;
[view addSubview:border];
[tab.tabBar setSelectionIndicatorImage:[self changeViewToImage:view]];
//This is the method that will draw the underline
-(UIImage ) changeViewToImage : (UIView ) viewForImage {
UIGraphicsBeginImageContext(viewForImage.bounds.size);
[viewForImage.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}

Just Place this code into Appdelegate.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
UIView *view1 = [[UIView alloc]initWithFrame:CGRectMake([UITabBar appearance].frame.origin.x,[UITabBar appearance].frame.origin.y, [[UIScreen mainScreen] bounds].size.width/3, 56)];
UIImageView *border = [[UIImageView alloc]initWithFrame:CGRectMake(view1.frame.origin.x,view1.frame.size.height-6, [[UIScreen mainScreen] bounds].size.width/3, 6)];
border.backgroundColor = [UIColor colorWithRed:255.0f/255.0f green:105.0f/255.0f blue:84.0f/255.0f alpha:1.0f];
[view1 addSubview:border];
UIImage *img=[self ChangeViewToImage:view1];
[[UITabBar appearance] setSelectionIndicatorImage:img];
[[UITabBar appearance] setTintColor: [UIColor colorWithRed:255.0f/255.0f green:105.0f/255.0f blue:84.0f/255.0f alpha:1.0f]];
return YES;
}
-(UIImage * ) ChangeViewToImage : (UIView *) viewForImage
{
UIGraphicsBeginImageContext(viewForImage.bounds.size);
[viewForImage.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}

In the AppDelegate didFininshLaunch Method Add Global Style for UITabbar
UITabBar.appearance().selectionIndicatorImage = UIImage.getImageWithColorPosition(UIColor.whiteColor(), size: CGSizeMake(kScreenWidth/4, 49), lineSize: CGSizeMake(kScreenWidth/4, 2))
class func getImageWithColorPosition(color: UIColor, size: CGSize, lineSize: CGSize) -> UIImage {
let rect = CGRectMake(0, 0, size.width, size.height)
let rectLine = CGRectMake(0, size.height-lineSize.height, lineSize.width, lineSize.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
UIColor.clearColor().setFill()
UIRectFill(rect)
color.setFill()
UIRectFill(rectLine)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
EDIT Swift 3
func getImageWithColorPosition(color: UIColor, size: CGSize, lineSize: CGSize) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
let rectLine = CGRect(x: 0, y: size.height-lineSize.height, width: lineSize.width, height: lineSize.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
UIColor.clear.setFill()
UIRectFill(rect)
color.setFill()
UIRectFill(rectLine)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}

Use this code to add a bottom border to any UIView object, just replace view with your UITabbarItem object:
CALayer *bottomBorder = [CALayer layer];
bottomBorder.frame = CGRectMake(0, view.frame.size.height - 1, view.frame.size.width - 1, 1);
bottomBorder.backgroundColor = [UIColor lightGrayColor].CGColor;
[view.layer addSublayer:bottomBorder];

Related

How to prevent bold images with UIImageRenderingModeAlwaysTemplate

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:
When the "bold text" is enabled:
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
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];
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)
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
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;
}
}

Transparent UIButton title

How can I subtract the shape of the title text from the button background?
I created a custom UIButton class. Currently the code to add the border and text color looks simply like this
layer.borderWidth = 2.0
layer.borderColor = buttonColor.CGColor
layer.cornerRadius = (CGFloat(bounds.height) + paddingVertical * 2.0) / 2.0
setTitleColor(buttonColor, forState: UIControlState.Normal)
Any advice on how I can subtract the button title shape from background. Do I need to render all this as an image or is there a better alternative?
Extension for swift 4
extension UIButton{
func clearColorForTitle() {
let buttonSize = bounds.size
if let font = titleLabel?.font{
let attribs = [NSAttributedStringKey.font: font]
if let textSize = titleLabel?.text?.size(withAttributes: attribs){
UIGraphicsBeginImageContextWithOptions(buttonSize, false, UIScreen.main.scale)
if let ctx = UIGraphicsGetCurrentContext(){
ctx.setFillColor(UIColor.white.cgColor)
let center = CGPoint(x: buttonSize.width / 2 - textSize.width / 2, y: buttonSize.height / 2 - textSize.height / 2)
let path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: buttonSize.width, height: buttonSize.height))
ctx.addPath(path.cgPath)
ctx.fillPath()
ctx.setBlendMode(.destinationOut)
titleLabel?.text?.draw(at: center, withAttributes: [NSAttributedStringKey.font: font])
if let viewImage = UIGraphicsGetImageFromCurrentImageContext(){
UIGraphicsEndImageContext()
let maskLayer = CALayer()
maskLayer.contents = ((viewImage.cgImage) as AnyObject)
maskLayer.frame = bounds
layer.mask = maskLayer
}
}
}
}
}
Usage :
button.clearColorForTitle()
I've written some code for you, my UIButton subclass called AKStencilButton (available on github https://github.com/purrrminator/AKStencilButton):
#import "AKStencilButton.h"
#import <QuartzCore/QuartzCore.h>
#interface AKStencilButton ()
{
UIColor * _buttonColor;
}
#end
#implementation AKStencilButton
-(id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]){
[self setupDefaults];
}
return self;
}
-(instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]){
[self setupDefaults];
}
return self;
}
-(void)setupDefaults
{
self.backgroundColor = [UIColor redColor];
self.layer.cornerRadius = 4;
self.clipsToBounds = YES;
}
-(void)layoutSubviews
{
[super layoutSubviews];
[self refreshMask];
}
-(void)setTitleColor:(UIColor *)color forState:(UIControlState)state
{
[super setTitleColor:[UIColor clearColor] forState:state];
}
-(void)refreshMask
{
self.titleLabel.backgroundColor = [UIColor clearColor];
[self setTitleColor:[UIColor clearColor] forState:UIControlStateNormal];
NSString * text = self.titleLabel.text;
CGSize buttonSize = self.bounds.size;
UIFont * font = self.titleLabel.font;
NSDictionary* attribs = #{NSFontAttributeName: self.titleLabel.font};
CGSize textSize = [text sizeWithAttributes:attribs];
UIGraphicsBeginImageContextWithOptions(buttonSize, NO, [[UIScreen mainScreen] scale]);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(ctx, [UIColor whiteColor].CGColor);
CGPoint center = CGPointMake(buttonSize.width/2-textSize.width/2, buttonSize.height/2-textSize.height/2);
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, buttonSize.width, buttonSize.height)];
CGContextAddPath(ctx, path.CGPath);
CGContextFillPath(ctx);
CGContextSetBlendMode(ctx, kCGBlendModeDestinationOut);
[text drawAtPoint:center withAttributes:#{NSFontAttributeName:font}];
UIImage* viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CALayer *maskLayer = [CALayer layer];
maskLayer.contents = (__bridge id)(viewImage.CGImage);
maskLayer.frame = self.bounds;
self.layer.mask = maskLayer;
}
#end
it looks like this (sorry for the rainbow):
I added the following method to UIView to solve this:
-(void)maskSubviews:(NSArray*)subviews {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
CGContextFillRect(context, self.bounds);
CGContextSetBlendMode(context, kCGBlendModeDestinationOut);
for (UIView *view in subviews) {
CGPoint origin = [view convertPoint:CGPointZero toView:self];
CGContextTranslateCTM(context, origin.x, origin.y);
[view.layer renderInContext:context];
CGContextTranslateCTM(context, -origin.x, -origin.y);
}
UIImage* mask = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.maskView = [[UIImageView alloc] initWithImage:mask];
}
Simply call it with the views you want masked out. For example:
[button maskSubviews: #[button.titleLabel, button.imageView]]
I converted #purrrminator's solution to Swift 3, just the main method.
func clearTextButton(button: UIButton, title: NSString, color: UIColor) {
button.backgroundColor = color
button.titleLabel?.backgroundColor = UIColor.clear()
button.setTitleColor(UIColor.clear(), for: .normal)
button.setTitle(title as String, for: [])
let buttonSize: CGSize = button.bounds.size
let font: UIFont = button.titleLabel!.font
let attribs: [String : AnyObject] = [NSFontAttributeName: button.titleLabel!.font]
let textSize: CGSize = title.size(attributes: attribs)
UIGraphicsBeginImageContextWithOptions(buttonSize, false, UIScreen.main().scale)
let ctx: CGContext = UIGraphicsGetCurrentContext()!
ctx.setFillColor(UIColor.white().cgColor)
let center: CGPoint = CGPoint(x: buttonSize.width / 2 - textSize.width / 2, y: buttonSize.height / 2 - textSize.height / 2)
let path: UIBezierPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: buttonSize.width, height: buttonSize.height))
ctx.addPath(path.cgPath)
ctx.fillPath()
ctx.setBlendMode(.destinationOut)
title.draw(at: center, withAttributes: [NSFontAttributeName: font])
let viewImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
let maskLayer: CALayer = CALayer()
maskLayer.contents = ((viewImage.cgImage) as! AnyObject)
maskLayer.frame = button.bounds
button.layer.mask = maskLayer
}

Add text on custom marker on google map for ios

Am trying to put marker with Textview .Is there any posibility to add text over marker on google map in ios.
Like This
You must make a view, where you must create an imageView (with your marker image) and Label (with your text) and take a screenshot of that view, and set as icon to your GMSMarker.
Something like this:
- (void)foo
{
GMSMarker *marker = [GMSMarker new];
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0,0,60,60)];
UIImageView *pinImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"myPin"]];
UILabel *label = [UILabel new];
label.text = #"1";
//label.font = ...;
[label sizeToFit];
[view addSubview:pinImageView];
[view addSubview:label];
//i.e. customize view to get what you need
UIImage *markerIcon = [self imageFromView:view];
marker.icon = markerIcon;
marker.map = self.mapView;
}
- (UIImage *)imageFromView:(UIView *) view
{
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)]) {
UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, [[UIScreen mainScreen] scale]);
} else {
UIGraphicsBeginImageContext(view.frame.size);
}
[view.layer renderInContext: UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
If you want to display something like this , then just follow these steps. It is very simple, You can use this method.
-(UIImage *)createImage:(NSUInteger)count{ //count is the integer that has to be shown on the marker
UIColor *color = [UIColor redColor]; // select needed color
NSString *string = [NSString stringWithFormat:#"%lu",(unsigned long)count]; // the string to colorize
NSDictionary *attrs = #{ NSForegroundColorAttributeName : color };
NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:string attributes:attrs]; // add Font according to your need
UIImage *image = [UIImage imageNamed:#"ic_marker_orange"]; // The image on which text has to be added
UIGraphicsBeginImageContext(image.size);
[image drawInRect:CGRectMake(0,0,image.size.width,image.size.height)];
CGRect rect = CGRectMake(20,5, image.size.width, image.size.height);// change the frame of your text from here
[[UIColor whiteColor] set];
[attrStr drawInRect:rect];
UIImage *markerImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return markerImage;}
and when you set marker to the map then just set
GMSMarker *marker = [[GMSMarker alloc] init];
marker.icon = [self createImage:[model.strFriendCount integerValue]]; // pass any integer to the method.
Here is the Swift 5 version of Kunal's answer:
//count is the integer that has to be shown on the marker
func createImage(_ count: Int) -> UIImage {
let color = UIColor.red
// select needed color
let string = "\(UInt(count))"
// the string to colorize
let attrs: [NSAttributedString.Key: Any] = [.foregroundColor: color]
let attrStr = NSAttributedString(string: string, attributes: attrs)
// add Font according to your need
let image = UIImage(named: "ic_marker_orange")!
// The image on which text has to be added
UIGraphicsBeginImageContext(image.size)
image.draw(in: CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(image.size.width), height: CGFloat(image.size.height)))
let rect = CGRect(x: CGFloat(20), y: CGFloat(5), width: CGFloat(image.size.width), height: CGFloat(image.size.height))
attrStr.draw(in: rect)
let markerImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return markerImage
}
Hope this helps someone else.
func createImage(_ count: Int) -> UIImage? {
let string = String(count)
let paragraphStyle = NSMutableParagraphStyle()
let font = UIFont.systemFont(ofSize: 9.5)
paragraphStyle.alignment = .center
let attributes: [NSAttributedString.Key: Any] = [
.font : font,
.foregroundColor: UIColor.black,
.paragraphStyle: paragraphStyle
]
let attrStr = NSAttributedString(string: string, attributes: attributes)
let image = self.imageWithImage(image: UIImage(named: "pin_online.png")!, scaledToSize: CGSize(width: 50.0, height: 60.0))
UIGraphicsBeginImageContext(image.size)
UIGraphicsBeginImageContextWithOptions(image.size, false, 0.0)
image.draw(in: CGRect(x: 0, y: 0, width: CGFloat(image.size.width), height: CGFloat(image.size.height)))
let rect = CGRect(x: 14.7, y: CGFloat(3), width: CGFloat(image.size.width), height: CGFloat(image.size.height))
attrStr.draw(in: rect)
let markerImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return markerImage
}

UIGraphicsBeginImageContextWithOptions not capturing shadows / gradients

THE PROBLEM
I'm using UIGraphicsBeginImageContextWithOptions to save a screenshot into a UIImage. While this works, the resulting UIImage is missing any shadows and gradients.
How do I get it to also screenshot the layer stuff?
TEST CODE SHOWING THE PROBLEM
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#implementation ViewController
- (void)viewDidLoad {
//super
[super viewDidLoad];
//background
self.view.backgroundColor = [UIColor whiteColor];
//container
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 300, 200)];
container.backgroundColor = [UIColor blackColor];
[self.view addSubview:container];
//circle
UIView *circle = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 10, 10)];
circle.backgroundColor = [UIColor whiteColor];
circle.layer.cornerRadius = 5;
circle.layer.shadowColor = [UIColor redColor].CGColor;
circle.layer.shadowOffset = CGSizeZero;
circle.layer.shadowOpacity = 1;
circle.layer.shadowRadius = 10;
circle.layer.shadowPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-10, -10, 30, 30)].CGPath;
[container addSubview:circle];
//screenshot
UIGraphicsBeginImageContextWithOptions(container.bounds.size, container.opaque, 0.0);
[container.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//image view
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(10, 568-200-20-10, 300, 200)];
iv.image = screenshot;
[self.view addSubview:iv];
}
#end
SIMULATOR SCREENSHOT SHOWING THE PROBLEM
The top area is the original view. The bottom area is a UIImageView with the screenshot.
In this line ,
UIGraphicsBeginImageContextWithOptions(container.bounds.size, container.opaque, 0.0);
put NO instead of container.opaque
Use this method ,
- (UIImage *)imageOfView:(UIView *)view
{
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)]) {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.0);
} else {
UIGraphicsBeginImageContext(view.bounds.size);
}
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
For drop shadow on an Image:
public func generateShadow(for image: UIImage, shadowOffset: CGSize,blur: CGFloat, shadowColor: CGColor ) -> UIImage? {
let size = CGSize(width: image.size.width + blur, height:
image.size.height + blur)
return MediaPickerFramework.generateImage(CGSize(width: size.width, height: size.width), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setShadow(offset: shadowOffset,
blur: blur,
color: shadowColor)
let center = CGPoint(x: size.width / 2, y: size.height / 2)
let imageOrigin = CGPoint(x: center.x - (image.size.width / 2), y: center.y - (image.size.height / 2))
context.draw(image.cgImage!,
in: CGRect(x: imageOrigin.x, y: imageOrigin.y, width: image.size.width, height: image.size.height),
byTiling:false)
})
}
and for draw Gradient on an image, you can use the below function
func generateGradientTintedImage(image: UIImage?, colors: [UIColor]) -> UIImage? {
guard let image = image else {
return nil
}
let imageSize = image.size
UIGraphicsBeginImageContextWithOptions(imageSize, false, image.scale)
if let context = UIGraphicsGetCurrentContext() {
let imageRect = CGRect(origin: CGPoint(), size: imageSize)
context.saveGState()
context.translateBy(x: imageRect.midX, y: imageRect.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -imageRect.midX, y: -imageRect.midY)
context.clip(to: imageRect, mask: image.cgImage!)
let gradientColors = colors.map { $0.cgColor } as CFArray
let delta: CGFloat = 1.0 / (CGFloat(colors.count) - 1.0)
var locations: [CGFloat] = []
for i in 0 ..< colors.count {
locations.append(delta * CGFloat(i))
}
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: imageRect.height), end: CGPoint(x: 0.0, y: 0.0), options: CGGradientDrawingOptions())
context.restoreGState()
}
let tintedImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return tintedImage
}

How can I change image tintColor in iOS and WatchKit

I have an UIImageView called "theImageView", with UIImage in a single color (transparent background) just like the left black heart below. How can I change the tint color of this image programmatically in iOS 7 or above, as per the tint method used in the iOS 7+ Navigation Bar icons?
Can this method also work in WatchKit for an Apple Watch app?
iOS
For an iOS app, in Swift 3, 4 or 5:
theImageView.image = theImageView.image?.withRenderingMode(.alwaysTemplate)
theImageView.tintColor = UIColor.red
For Swift 2:
theImageView.image = theImageView.image?.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)
theImageView.tintColor = UIColor.redColor()
Meanwhile, the modern Objective-C solution is:
theImageView.image = [theImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[theImageView setTintColor:[UIColor redColor]];
Watchkit
In WatchKit for Apple Watch apps, you can set the tint color for a template image.
You must add your image to an Asset Catalog in your WatchKit App, and set the image set to be rendered as a Template Image in the Attributes Inspector. Unlike for an iPhone app, you cannot set the template rendering in code in the WatchKit Extension at present.
Set that image to be used in your WKInterfaceImage in interface builder for your app
Create an IBOutlet in your WKInterfaceController for the WKInterfaceImage called 'theImage'...
To then set the tint color in Swift 3 or 4:
theImage.setTintColor(UIColor.red)
Swift 2:
theImage.setTintColor(UIColor.redColor())
To then set the tint color in Objective-C:
[self.theImage setTintColor:[UIColor redColor]];
If you use a template image and do not apply a tint colour, the Global Tint for your WatchKit app will be applied. If you have not set a Global Tint, theImage will be tinted light blue by default when used as a template image.
Here's a category that should do the trick
#interface UIImage(Overlay)
#end
#implementation UIImage(Overlay)
- (UIImage *)imageWithColor:(UIColor *)color1
{
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);
[color1 setFill];
CGContextFillRect(context, rect);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
#end
so you would do:
theImageView.image = [theImageView.image imageWithColor:[UIColor redColor]];
I had to do this in Swift using an extension.
I thought I'd share how I did it:
extension UIImage {
func imageWithColor(color1: UIColor) -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
color1.setFill()
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)
CGContextFillRect(context, rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext() as UIImage
UIGraphicsEndImageContext()
return newImage
}
}
Usage:
theImageView.image = theImageView.image.imageWithColor(UIColor.redColor())
Swift 4
extension UIImage {
func imageWithColor(color1: UIColor) -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
color1.setFill()
let context = UIGraphicsGetCurrentContext()
context?.translateBy(x: 0, y: self.size.height)
context?.scaleBy(x: 1.0, y: -1.0)
context?.setBlendMode(CGBlendMode.normal)
let rect = CGRect(origin: .zero, size: CGSize(width: self.size.width, height: self.size.height))
context?.clip(to: rect, mask: self.cgImage!)
context?.fill(rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
Usage:
theImageView.image = theImageView.image?.imageWithColor(color1: UIColor.red)
In storyboard and image Assets. you can change this two also:
Update the Render Mode to Template Image
Update the tint Color in Views.
If anyone care a solution without UIImageView:
// (Swift 3)
extension UIImage {
func tint(with color: UIColor) -> UIImage {
var image = withRenderingMode(.alwaysTemplate)
UIGraphicsBeginImageContextWithOptions(size, false, scale)
color.set()
image.draw(in: CGRect(origin: .zero, size: size))
image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
}
Swift 4
Change tint of UIImage SVG / PDF, that work for image with unique color :
import Foundation
// MARK: - UIImage extensions
public extension UIImage {
//
/// Tint Image
///
/// - Parameter fillColor: UIColor
/// - Returns: Image with tint color
func tint(with fillColor: UIColor) -> UIImage? {
let image = withRenderingMode(.alwaysTemplate)
UIGraphicsBeginImageContextWithOptions(size, false, scale)
fillColor.set()
image.draw(in: CGRect(origin: .zero, size: size))
guard let imageColored = UIGraphicsGetImageFromCurrentImageContext() else {
return nil
}
UIGraphicsEndImageContext()
return imageColored
}
}
Change tint of UIImageView, that work for image with unique color :
let imageView = UIImageView(frame: CGRect(x: 50, y: 50, width: 50, height: 50))
imageView.image = UIImage(named: "hello.png")!.withRenderingMode(.alwaysTemplate)
imageView.tintColor = .yellow
Change tint of UIImage for picture, use that :
import Foundation
// MARK: - Extensions UIImage
public extension UIImage {
/// Tint, Colorize image with given tint color
/// This is similar to Photoshop's "Color" layer blend mode
/// This is perfect for non-greyscale source images, and images that
/// have both highlights and shadows that should be preserved<br><br>
/// white will stay white and black will stay black as the lightness of
/// the image is preserved
///
/// - Parameter TintColor: Tint color
/// - Returns: Tinted image
public func tintImage(with fillColor: UIColor) -> UIImage {
return modifiedImage { context, rect in
// draw black background - workaround to preserve color of partially transparent pixels
context.setBlendMode(.normal)
UIColor.black.setFill()
context.fill(rect)
// draw original image
context.setBlendMode(.normal)
context.draw(cgImage!, in: rect)
// tint image (loosing alpha) - the luminosity of the original image is preserved
context.setBlendMode(.color)
fillColor.setFill()
context.fill(rect)
// mask by alpha values of original image
context.setBlendMode(.destinationIn)
context.draw(context.makeImage()!, in: rect)
}
}
/// Modified Image Context, apply modification on image
///
/// - Parameter draw: (CGContext, CGRect) -> ())
/// - Returns: UIImage
fileprivate func modifiedImage(_ draw: (CGContext, CGRect) -> ()) -> UIImage {
// using scale correctly preserves retina images
UIGraphicsBeginImageContextWithOptions(size, false, scale)
let context: CGContext! = UIGraphicsGetCurrentContext()
assert(context != nil)
// correctly rotate image
context.translateBy(x: 0, y: size.height)
context.scaleBy(x: 1.0, y: -1.0)
let rect = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
draw(context, rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
With Swift
let commentImageView = UIImageView(frame: CGRectMake(100, 100, 100, 100))
commentImageView.image = UIImage(named: "myimage.png")!.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)
commentImageView.tintColor = UIColor.blackColor()
addSubview(commentImageView)
For swift 3 purposes
theImageView.image = theImageView.image!.withRenderingMode(.alwaysTemplate)
theImageView.tintColor = UIColor.red
Here's a simple extension that works for Swift 5:
extension UIImageView {
func setImageTintColor(_ color: UIColor) {
let tintedImage = self.image?.withRenderingMode(.alwaysTemplate)
self.image = tintedImage
self.tintColor = color
}
}
Usage:
myImageView.setImageTintColor(.systemBlue)
Also, for the above answers, in iOS 13 and later there is a clean way
let image = UIImage(named: "imageName")?.withTintColor(.white, renderingMode: .alwaysTemplate)
Try this
http://robots.thoughtbot.com/designing-for-ios-blending-modes
or
- (void)viewDidLoad
{
[super viewDidLoad];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 30, 300, 50)];
label.numberOfLines = 0;
label.font = [UIFont systemFontOfSize:13];
label.text = #"These checkmarks use the same gray checkmark image with a tintColor applied to the image view";
[self.view addSubview:label];
[self _createImageViewAtY:100 color:[UIColor purpleColor]];
}
- (void)_createImageViewAtY:(int)y color:(UIColor *)color {
UIImage *image = [[UIImage imageNamed:#"gray checkmark.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
CGRect frame = imageView.frame;
frame.origin.x = 100;
frame.origin.y = y;
imageView.frame = frame;
if (color)
imageView.tintColor = color;
[self.view addSubview:imageView];
}
For tinting the image of a UIButton
let image1 = "ic_shopping_cart_empty"
btn_Basket.setImage(UIImage(named: image1)?.withRenderingMode(.alwaysTemplate), for: .normal)
btn_Basket.setImage(UIImage(named: image1)?.withRenderingMode(.alwaysTemplate), for: .selected)
btn_Basket.imageView?.tintColor = UIColor(UIColor.Red)
iOS
Solution for doing it from Interface Builder, set templateImage param in keyPath and choose your tint color from IB
extension UIImageView {
// make template image with tint color
var templateImage: Bool {
set {
if newValue, let image = self.image {
let newImage = image.withRenderingMode(.alwaysTemplate)
self.image = newImage
}
} get {
return false
}
}
}
Take benefit of Extension in Swift :-
extension UIImageView {
func changeImageColor( color:UIColor) -> UIImage
{
image = image!.withRenderingMode(.alwaysTemplate)
tintColor = color
return image!
}
}
//Change color of logo
logoImage.image = logoImage.changeImageColor(color: .red)
Swift 3 version of extension answer from fuzz
func imageWithColor(color: UIColor) -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
color.setFill()
let context = UIGraphicsGetCurrentContext()! as CGContext
context.translateBy(x: 0, y: self.size.height)
context.scaleBy(x: 1.0, y: -1.0);
context.setBlendMode(.normal)
let rect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height) as CGRect
context.clip(to: rect, mask: self.cgImage!)
context.fill(rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()! as UIImage
UIGraphicsEndImageContext()
return newImage
}
With iOS 13 and above, you can simply use
let image = UIImage(named: "Heart")?.withRenderingMode(.alwaysTemplate)
if #available(iOS 13.0, *) {
imageView.image = image?.withTintColor(UIColor.white)
}
There is a native method for UIImage since iOS 13
let image = yourImage.withTintColor(.systemRed)
This is my UIImage extension and you can directly use changeTintColor function for an image.
extension UIImage {
func changeTintColor(color: UIColor) -> UIImage {
var newImage = self.withRenderingMode(.alwaysTemplate)
UIGraphicsBeginImageContextWithOptions(self.size, false, newImage.scale)
color.set()
newImage.draw(in: CGRect(x: 0.0, y: 0.0, width: self.size.width, height: self.size.height))
newImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
func changeColor(color: UIColor) -> UIImage {
let backgroundSize = self.size
UIGraphicsBeginImageContext(backgroundSize)
guard let context = UIGraphicsGetCurrentContext() else {
return self
}
var backgroundRect = CGRect()
backgroundRect.size = backgroundSize
backgroundRect.origin.x = 0
backgroundRect.origin.y = 0
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0
color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
context.setFillColor(red: red, green: green, blue: blue, alpha: alpha)
context.translateBy(x: 0, y: backgroundSize.height)
context.scaleBy(x: 1.0, y: -1.0)
context.clip(to: CGRect(x: 0.0, y: 0.0, width: self.size.width, height: self.size.height),
mask: self.cgImage!)
context.fill(backgroundRect)
var imageRect = CGRect()
imageRect.size = self.size
imageRect.origin.x = (backgroundSize.width - self.size.width) / 2
imageRect.origin.y = (backgroundSize.height - self.size.height) / 2
context.setBlendMode(.multiply)
context.draw(self.cgImage!, in: imageRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
Example usage like this
let image = UIImage(named: "sample_image")
imageView.image = image.changeTintColor(color: UIColor.red)
And you can use change changeColor function to change the image color
Swift 5
Redrawing image with background and fill color
extension UIImage {
func withBackground(color: UIColor, fill fillColor: UIColor) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, true, scale)
guard let ctx = UIGraphicsGetCurrentContext(), let image = cgImage else { return self }
defer { UIGraphicsEndImageContext() }
ctx.concatenate(CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: size.height))
let rect = CGRect(origin: .zero, size: size)
// draw background
ctx.setFillColor(color.cgColor)
ctx.fill(rect)
// draw image with fill color
ctx.clip(to: rect, mask: image)
ctx.setFillColor(fillColor.cgColor)
ctx.fill(rect)
return UIGraphicsGetImageFromCurrentImageContext() ?? self
}
}
Now i use this method based in Duncan Babbage response:
+ (UIImageView *) tintImageView: (UIImageView *)imageView withColor: (UIColor*) color{
imageView.image = [imageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[imageView setTintColor:color];
return imageView;
}
You can use this in Swift 3 if you have an image to replace the clear button
func addTextfieldRightView(){
let rightViewWidth:CGFloat = 30
let viewMax = self.searchTxt.frame.height
let buttonMax = self.searchTxt.frame.height - 16
let buttonView = UIView(frame: CGRect(
x: self.searchTxt.frame.width - rightViewWidth,
y: 0,
width: viewMax,
height: viewMax))
let myButton = UIButton(frame: CGRect(
x: (viewMax - buttonMax) / 2,
y: (viewMax - buttonMax) / 2,
width: buttonMax,
height: buttonMax))
myButton.setImage(UIImage(named: "BlueClear")!, for: .normal)
buttonView.addSubview(myButton)
let clearPressed = UITapGestureRecognizer(target: self, action: #selector(SearchVC.clearPressed(sender:)))
buttonView.isUserInteractionEnabled = true
buttonView.addGestureRecognizer(clearPressed)
myButton.addTarget(self, action: #selector(SearchVC.clearPressed(sender:)), for: .touchUpInside)
self.searchTxt.rightView = buttonView
self.searchTxt.rightViewMode = .whileEditing
}
Subclass which can be used from code and Interface Builder as well:
#implementation TintedImageView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self setup];
}
return self;
}
-(void)setup {
self.image = [self.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
}
#end
profileImageView.image = theImageView.image!.withRenderingMode(.alwaysTemplate)
profileImageView.tintColor = UIColor.green
OR
First select Particular image in image asset and then select reddened as Template instead of Default and after that write line.
profileImageView.tintColor = UIColor.green
if you have any id for the SVG image, you can fill the colours with respect to the ID.
let image = SVGKImage(named: "iconName")
let svgIMGV = SVGKFastImageView(frame: self.imgView.frame)
svgIMGV.image = image
svgIMGV.fillTintColor(colorImage: UIColor.red, iconID: "Bank")
// Add in extension SVGKImageView
extension SVGKImageView {
func fillTintColor(colorImage: UIColor, iconID: String) {
if self.image != nil && self.image.caLayerTree != nil {
print(self.image.caLayerTree.sublayers)
guard let sublayers = self.image.caLayerTree.sublayers else { return }
fillRecursively(sublayers: sublayers, color: colorImage, iconID: iconID)
}
}
private func fillRecursively(sublayers: [CALayer], color: UIColor, iconID: String, hasFoundLayer: Bool) {
var isLayerFound = false
for layer in sublayers {
if let l = layer as? CAShapeLayer {
print(l.name)
//IF you want to color the specific shapelayer by id else remove the l.name == "myID" validation
if let name = l.name, hasFoundLayer == true && name == "myID" {
self.colorThatImageWIthColor(color: color, layer: l)
print("Colouring FInished")
}
} else {
if layer.name == iconID {
if let innerSublayer = layer.sublayers as? [CAShapeLayer] {
fillRecursively(sublayers: innerSublayer, color: color, iconID: iconID, hasFoundLayer: true )
print("FOund")
}
} else {
if let l = layer as? CALayer, let sub = l.sublayers {
fillRecursively(sublayers: sub, color: color, iconID: iconID, hasFoundLayer: false)
}
}
}
}
}
func colorThatImageWIthColor(color: UIColor, layer: CAShapeLayer) {
if layer.strokeColor != nil {
layer.strokeColor = color.cgColor
}
if layer.fillColor != nil {
layer.fillColor = color.cgColor
}
}
}
OR Checkout this example.
https://github.com/ravisfortune/SVGDEMO
let navHeight = self.navigationController?.navigationBar.frame.height;
let menuBtn = UIButton(type: .custom)
menuBtn.frame = CGRect(x: 0, y: 0, width: 45, height: navHeight!)
menuBtn.setImage(UIImage(named:"image_name")!.withRenderingMode(.alwaysTemplate), for: .normal)
menuBtn.tintColor = .black

Resources