ios UIImageRenderingModeAlwaysTemplate doesn't ignore opacity - ios

I am trying to dynamically change the color and opacity of the images given to me by the designer. Of course, it works seamlessly with the following code :
_imgViewForMenu.tintColor = [_lblForMenu.textColor colorWithAlphaComponent:1.0f];
// This alpha component wont affect the png image with 38% opacity.
// You will never get full black image with [UIColor blackColor]
// and alpha component 1.0
_imgViewForMenu.image = [imageForMenuIcon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
This will work, but only when the image has no opacity of its own. Else as said in the comment for the code, it wont work.
So the question is, how do you render an image to ignore both its color component as well as opacity. The system controls like UITabBar and UIBarButonItem seem to do it with ease. Why not with UIImageView then?

Try this:
extension UIImage {
func tinted(with color: UIColor) -> UIImage? {
let image = withRenderingMode(.alwaysTemplate)
UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale)
color.set()
image.draw(in: CGRect(x: 0.0, y: 0.0, width: image.size.width, height: image.size.height))
let tintedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return tintedImage
}
}

Related

Swift - How to change a png tint color without losing the clear background? [duplicate]

I am new to swift and trying to achieve this essentially,
This image to
This image ->
I am using this code from here to change the tint on image but do not get the desired output
func tint(image: UIImage, color: UIColor) -> UIImage
{
let ciImage = CIImage(image: image)
let filter = CIFilter(name: "CIMultiplyCompositing")
let colorFilter = CIFilter(name: "CIConstantColorGenerator")
let ciColor = CIColor(color: color)
colorFilter.setValue(ciColor, forKey: kCIInputColorKey)
let colorImage = colorFilter.outputImage
filter.setValue(colorImage, forKey: kCIInputImageKey)
filter.setValue(ciImage, forKey: kCIInputBackgroundImageKey)
return UIImage(CIImage: filter.outputImage)!
}
If this is a noob question, apologies. I was able to do this easily in javascript but not in swift.
hi you can use it the following way. First the UIImage Extension that you used needs some updates.
you can copy the code below for Swift 3
extension UIImage{
func tint(color: UIColor, blendMode: CGBlendMode) -> UIImage
{
let drawRect = CGRect(x: 0,y: 0,width: size.width,height: size.height)
UIGraphicsBeginImageContextWithOptions(size, false, scale)
color.setFill()
UIRectFill(drawRect)
draw(in: drawRect, blendMode: blendMode, alpha: 1.0)
let tintedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return tintedImage!
}
}
Then in your viewDidLoad where you are using the image
for example i used the image from IBOutlet imageWood
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.imageWood.image = self.imageWood.image?.tint(color: UIColor.green, blendMode: .saturation)
}
You will have to use the appropriate color and the images
The other Extension i found
extension UIImage {
// 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
// white will stay white and black will stay black as the lightness of the image is preserved
func tint(_ tintColor: 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(self.cgImage!, in: rect)
// tint image (loosing alpha) - the luminosity of the original image is preserved
context.setBlendMode(.color)
tintColor.setFill()
context.fill(rect)
// mask by alpha values of original image
context.setBlendMode(.destinationIn)
context.draw(self.cgImage!, in: rect)
}
}
// fills the alpha channel of the source image with the given color
// any color information except to the alpha channel will be ignored
func fillAlpha(_ fillColor: UIColor) -> UIImage {
return modifiedImage { context, rect in
// draw tint color
context.setBlendMode(.normal)
fillColor.setFill()
context.fill(rect)
// mask by alpha values of original image
context.setBlendMode(.destinationIn)
context.draw(self.cgImage!, in: rect)
}
}
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!
}
}
Using it like this
self.imageWood.image = self.imageWood.image?.tint(UIColor.purple.withAlphaComponent(1))
Try the below code, should work for you.
if let myImage = UIImage(named: "imageName")?.withRenderingMode(.alwaysTemplate) {
myImageView.image = myImage
myImageView.tintColor = UIColor.white
}
Your image is simple and only has one color. I would suggest making your image transparent except where the lines are, and then layer it on top of a white background to get your results.
It looks like you got the results you're after using drawing modes. There are also a number of Core image filters that let you apply tinting effects to images, as well as replacing specific colors. Those would be a good choice for more complex images.

I have a png file with no background, how can I create a clear color background for this image in iOS?

So I understand that a UIImage inherently doesn't have a background. As many of us know, a lot of PNG files don't have a background Color thus making it clear. I'm attempting to upload a png file that doesn't have a background color, thus a clear color. Yes, I know I can set the background Color myself in adobe or sketch, but I'm assuming that other users don't know how to do this.
Here is a screenshot of the png that I have created:
As you can see, it's just two lines that are unioned together so there's no background set.
Now below is a screenshot of the aftermath of using the imagePicker to choose this png image from my photo roll.
Notice that the area that is supposed to be transparent is actually black. I want to color in the black part and make it actually clearColor instead and keep the green cross as it is. Now, I'm not sure if the black color is actually even a black color because perhaps it's just empty space. Can I fill in the empty black space and turn it into a clear color?
Here's my code right now that isn't working very well:
func overlayImage(image: UIImage, color: UIColor) -> UIImage? {
let rect = CGRectMake(0, 0, image.size.width, image.size.height)
let backgroundView = UIView(frame: rect)
backgroundView.backgroundColor = color
UIGraphicsBeginImageContext(rect.size)
let gcSize: CGSize = backgroundView.frame.size
UIGraphicsBeginImageContext(gcSize)
let context: CGContextRef = UIGraphicsGetCurrentContext()!
backgroundView.layer.renderInContext(context)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
Any help in either obj-C or Swift would be greatly appreciated.
I got rid of the overlay method above and am using the code below:
Updated with new code that still doesn't work
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage!, editingInfo: [NSObject : AnyObject]!) {
scaleImage(overlayImage(image, color: UIColor.clearColor()))
}
func scaleImageAndAddAugmented(image: UIImage?) {
let rect = CGRectMake(0, 0, image!.size.width, image!.size.height)
let backgroundView = UIView(frame: rect)
backgroundView.backgroundColor = UIColor.clearColor()
let size = CGSizeApplyAffineTransform(image!.size, CGAffineTransformMakeScale(0.25, 0.25))
let hasAlpha = false
let scale: CGFloat = 0.0
UIGraphicsBeginImageContextWithOptions(size, !hasAlpha, scale)
image!.drawInRect(CGRect(origin: CGPointZero, size: size))
let context = CGBitmapContextCreate(nil, Int(image!.size.width), Int(image!.size.height), 8, 0, CGColorSpaceCreateDeviceRGB(), CGImageAlphaInfo.PremultipliedLast.rawValue)
let myGeneratedImage = CGBitmapContextCreateImage(context)
CGContextDrawImage(context, rect, myGeneratedImage)
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.dismissViewControllerAnimated(true, completion: nil)
// set image below
}
It seems a bit of excess work to use UIGraphicsGetImageFromCurrentImageContext to generate an image from context, when you can just create an image from a file.
As first mentioned it is required to have Opaque = NO:
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
My bet is its the image itself, because the whole thing is faded.
Make 100% certain that the passed colour is a clear colour for:
backgroundView.backgroundColor = color
You could create a bitmap context and use that instead:
CGContextRef context = CGBitmapContextCreate(NULL,
width,
height,
8,
0,
rgbColorSpace,
kCGImageAlphaPremultipliedLast);
Rather than using the current context and you can use:
myGeneratedImage = CGBitmapContextCreateImage(context)
Drawing is easy as pie:
CGContextDrawImage(realContext,bounds,myGeneratedImage)
Swift code below:
func scaleImage(image: UIImage?) {
if let image = image {
let rect = CGRectMake(0, 0, image.size.width, image.size.height)
let size: CGSize = CGSizeApplyAffineTransform(image.size, CGAffineTransformMakeScale(0.25, 0.25))
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
image.drawInRect(CGRect(origin: CGPointZero, size: size))
let context = CGBitmapContextCreate(nil, Int(image.size.width), Int(image.size.height), 8, 0, CGColorSpaceCreateDeviceRGB(), CGImageAlphaInfo.PremultipliedLast.rawValue)
let myGeneratedImage = CGBitmapContextCreateImage(context)
CGContextDrawImage(context, rect, myGeneratedImage)
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
}

Swift - Core graphics and setting background color?

I created this method that generates and image out of an original one but adds padding to the new image according to a given size. Everything works fine except the background color of the image is always black although I set white as fill color. Any idea how to fix this?
public extension UIImage {
public func imageCenteredInParentWithSize(size: CGSize, backgroundColor: UIColor = UIColor.clearColor()) -> UIImage {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(size.width, size.height), true, 0.0)
let context = UIGraphicsGetCurrentContext()
UIGraphicsPushContext(context);
let origin = CGPointMake(
(size.width - self.size.width) / 2.0,
(size.height - self.size.height) / 2.0
)
backgroundColor.setFill()
drawAtPoint(origin)
UIGraphicsPopContext()
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
EDIT:
Here is the working version
public func imageCenteredInParentWithSize(size: CGSize, backgroundColor: UIColor = UIColor.clearColor()) -> UIImage {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(size.width, size.height), true, UIScreen.mainScreen().scale)
let context = UIGraphicsGetCurrentContext()
let origin = CGPointMake(
(size.width - self.size.width) / 2.0,
(size.height - self.size.height) / 2.0
)
backgroundColor.setFill()
CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height))
drawAtPoint(origin)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
The line backgroundColor.setFill() just sets the fill color of the current context, it doesn't actually do the filling. One way of performing the fill is to call CGContextFillRect(context, CGRect(x: 0, y: 0, width: size.width, height: size.height)) after setting the fill color.
Additionally, you should probably pass the scale of the UIImage as the scale parameter in UIGraphicsBeginImageContextWithOptions rather than 0.0. Also, you don't need the push- and pop-context lines at all - you're already working the in current context.
Looks to me like you're setting your background color to "Clear" (as in no color) which means it will show whatever color is set on the background view... which is almost certainly black.
You need to set the default to "UIColor.whiteColor()".

Change color of png in buttons - ios

I've got a set of icons that I've created that are transparent white PNGs:
And what I'd like to do is be able to tint them to other colors. Such as blue, grey, etc.
I've noticed that 'clicked/tapped' they change automatically to a grey. So I assume I can change that grey to whatever I like either with a tap or its normal state:
What would be the best way to achieve this?
Following code will set tint colour for normal state of button:
For Swift 4 and newer:
let origImage = UIImage(named: "imageName")
let tintedImage = origImage?.withRenderingMode(.alwaysTemplate)
btn.setImage(tintedImage, for: .normal)
btn.tintColor = .red
You can change tint colour according to your need when state changes for button.
Older versions
For Swift 3:
let origImage = UIImage(named: "imageName")
let tintedImage = origImage?.withRenderingMode(.alwaysTemplate)
btn.setImage(tintedImage, forState: .normal)
btn.tintColor = .redColor
For Swift 2:
see revision history.
I found the easiest approach below,
Open assetcatalog and select the image then go to attributes inspector and change Render As to Template Image as below
Then add below code in button Action method
yourButton.tintColor = .gray
Swift 4 or 5
extension UIButton{
func setImageTintColor(_ color: UIColor) {
let tintedImage = self.imageView?.image?.withRenderingMode(.alwaysTemplate)
self.setImage(tintedImage, for: .normal)
self.tintColor = color
}
}
Use:
button.setImage(UIImage(named: "image_name"), for: .normal) // You can set image direct from Storyboard
button.setImageTintColor(UIColor.white)
iOS 7 introduced a property called tintColor for views (including UIImageView). However you also need to set the rendering type on the UIImage for this to have any effect.
UIImage *originalImage = [UIImage imageNamed:#"image.png"];
UIImage *tintedImage = [originalImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIImageView *imageView = [[UIImageView alloc] initWithImage:tintedImage];
imageView.tintColor = [UIColor grayColor];
[self.view addSubview:imageView];
This should produce the effect you are after in a default state.
If you are setting the image for a button, just go to attributes inspector and change the button type to system. Then set the image and change the tint color. The color of the image will change. If it did not take place, check the button type.
For change tint of image (pick, classical image, photo) use that :
Example image :
Swift 2
public extension UIImage {
/**
Tint, Colorize image with given tint color<br><br>
This is similar to Photoshop's "Color" layer blend mode<br><br>
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<br><br>
<img src="http://yannickstephan.com/easyhelper/tint1.png" height="70" width="120"/>
**To**
<img src="http://yannickstephan.com/easyhelper/tint2.png" height="70" width="120"/>
- parameter tintColor: UIColor
- returns: UIImage
*/
public func tintPhoto(tintColor: UIColor) -> UIImage {
return modifiedImage { context, rect in
// draw black background - workaround to preserve color of partially transparent pixels
CGContextSetBlendMode(context, .Normal)
UIColor.blackColor().setFill()
CGContextFillRect(context, rect)
// draw original image
CGContextSetBlendMode(context, .Normal)
CGContextDrawImage(context, rect, self.CGImage)
// tint image (loosing alpha) - the luminosity of the original image is preserved
CGContextSetBlendMode(context, .Color)
tintColor.setFill()
CGContextFillRect(context, rect)
// mask by alpha values of original image
CGContextSetBlendMode(context, .DestinationIn)
CGContextDrawImage(context, rect, self.CGImage)
}
}
/**
Tint Picto to color
- parameter fillColor: UIColor
- returns: UIImage
*/
public func tintPicto(fillColor: UIColor) -> UIImage {
return modifiedImage { context, rect in
// draw tint color
CGContextSetBlendMode(context, .Normal)
fillColor.setFill()
CGContextFillRect(context, rect)
// mask by alpha values of original image
CGContextSetBlendMode(context, .DestinationIn)
CGContextDrawImage(context, rect, self.CGImage)
}
}
/**
Modified Image Context, apply modification on image
- parameter draw: (CGContext, CGRect) -> ())
- returns: UIImage
*/
private func modifiedImage(#noescape draw: (CGContext, CGRect) -> ()) -> UIImage {
// using scale correctly preserves retina images
UIGraphicsBeginImageContextWithOptions(size, false, scale)
let context: CGContext! = UIGraphicsGetCurrentContext()
assert(context != nil)
// correctly rotate image
CGContextTranslateCTM(context, 0, size.height);
CGContextScaleCTM(context, 1.0, -1.0);
let rect = CGRectMake(0.0, 0.0, size.width, size.height)
draw(context, rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
UPD
Swift 3
extension UIImage {
/**
Tint, Colorize image with given tint color<br><br>
This is similar to Photoshop's "Color" layer blend mode<br><br>
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<br><br>
<img src="http://yannickstephan.com/easyhelper/tint1.png" height="70" width="120"/>
**To**
<img src="http://yannickstephan.com/easyhelper/tint2.png" height="70" width="120"/>
- parameter tintColor: UIColor
- returns: UIImage
*/
func tintPhoto(_ tintColor: 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)
tintColor.setFill()
context.fill(rect)
// mask by alpha values of original image
context.setBlendMode(.destinationIn)
context.draw(context.makeImage()!, in: rect)
}
}
/**
Tint Picto to color
- parameter fillColor: UIColor
- returns: UIImage
*/
func tintPicto(_ fillColor: UIColor) -> UIImage {
return modifiedImage { context, rect in
// draw tint color
context.setBlendMode(.normal)
fillColor.setFill()
context.fill(rect)
// mask by alpha values of original image
context.setBlendMode(.destinationIn)
context.draw(cgImage!, 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!
}
}
You can use this extension:
import UIKit
extension CGContext {
func fill(_ rect: CGRect,
with mask: CGImage,
using color: CGColor) {
saveGState()
defer { restoreGState() }
translateBy(x: 0.0, y: rect.size.height)
scaleBy(x: 1.0, y: -1.0)
setBlendMode(.normal)
clip(to: rect, mask: mask)
setFillColor(color)
fill(rect)
}
}
extension UIImage {
func filled(with color: UIColor) -> UIImage {
let rect = CGRect(origin: .zero, size: self.size)
guard let mask = self.cgImage else { return self }
if #available(iOS 10.0, *) {
let rendererFormat = UIGraphicsImageRendererFormat()
rendererFormat.scale = self.scale
let renderer = UIGraphicsImageRenderer(size: rect.size,
format: rendererFormat)
return renderer.image { context in
context.cgContext.fill(rect,
with: mask,
using: color.cgColor)
}
} else {
UIGraphicsBeginImageContextWithOptions(rect.size,
false,
self.scale)
defer { UIGraphicsEndImageContext() }
guard let context = UIGraphicsGetCurrentContext() else { return self }
context.fill(rect,
with: mask,
using: color.cgColor)
return UIGraphicsGetImageFromCurrentImageContext() ?? self
}
}
}
If you use asset catalogs you can set the image asset itself to render in template mode. After that you can set the tintColor of the button in Interface Builder (or in code) and it should take.
Swift 4
let origImage = UIImage(named: "check")
let tintedImage = origImage?.withRenderingMode(.alwaysTemplate)
buttons[0].setImage(tintedImage, for: .normal)
buttons[0].tintColor = .red
If you use asset catalogs you can set the image asset itself to render in template mode. After that you can set the tintColor of the button in Interface Builder (or in code) and it should take.
Swift 4 and 4.2
let img = UIImage.init(named: "buttonName")?.withRenderingMode(UIImageRenderingMode.alwaysTemplate)
btn.setImage(img, for: .normal)
btn.tintColor = .gray

Setting UISearchBar's search field background image changes the padding

When I set a search field background image on a UIImage, the padding between the magnifying glass and placeholder text in the search bar when selected changes.
With the default background:
With a custom background:
This change is caused by these two lines:
UIImage *colorImage = [UIImage imageWithColor:[UIColor grayColor] size:CGSizeMake(28, 28)];
[self setSearchFieldBackgroundImage:[colorImage imageWithRoundedCorners:5] forState:UIControlStateNormal];
imageWithRoundedCorners: is a category method that simply draws the image onto a CALayer with a corner radius and then creates a UIImage from the graphics context.
Why is this and how can I avoid this? I tried passing an explicitly resizable image, but that had no effect.
Its odd that it resets it, however you can use something like this to set it to your liking after setting the background image. searchTextPositionAdjustment is a property on UISearchBar and works perfectly. 8.0 seems to be the default, but you could set it to whatever you like.
[self.searchBar setSearchFieldBackgroundImage:[self imageWithColor:[UIColor grayColor] andSize:CGSizeMake(28.0, 28.0)] forState:UIControlStateNormal];
[self.searchBar setSearchTextPositionAdjustment:UIOffsetMake(8.0, 0.0)];
For all those now using Swift, you can use the following code (works in Swift 2)
let searchBarBackground = UIImage.roundedImage(UIImage.imageWithColor(UIColor.whiteColor(), size: CGSize(width: 28, height: 28)),cornerRadius: 2)
searchBar.setSearchFieldBackgroundImage(searchBarBackground, forState: .Normal)
searchBar.searchTextPositionAdjustment = UIOffsetMake(8.0, 0.0)
Use this with an extension to UIImage:
extension UIImage {
class func imageWithColor(color: UIColor, size: CGSize) -> UIImage {
let rect: CGRect = CGRectMake(0, 0, size.width, size.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
UIRectFill(rect)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
class func roundedImage(image: UIImage, cornerRadius: Int) -> UIImage {
let rect = CGRect(origin:CGPoint(x: 0, y: 0), size: image.size)
UIGraphicsBeginImageContextWithOptions(image.size, false, 1)
UIBezierPath(
roundedRect: rect,
cornerRadius: CGFloat(cornerRadius)
).addClip()
image.drawInRect(rect)
return UIGraphicsGetImageFromCurrentImageContext()
}
}
To give something looking like this:
Updated solution for Swift 4
searchTextPositionAdjustment = UIOffset(horizontal: 8.0, vertical: 0.0)

Resources