Question or problem in the Swift programming language:
Is there a way to tint the images in an animation?
I know I can tint a single image like this:
var imageOne:UIImage = UIImage(named: "pullto_1.png")!; imageOne = imageOne.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate); refSequence.image = imageOne;
But when I try to do it like this it just dosen’t work:
var imageOne:UIImage = UIImage(named: "pullto_1.png")!; imageOne = imageOne.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate) var image2:UIImage = UIImage(named: "pullto_2.png")!; image2 = image2.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate) var image3:UIImage = UIImage(named: "pullto_3.png")!; image3 = image3.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate) var image4:UIImage = UIImage(named: "pullto_4.png")!; image4 = image4.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate) refSequence.animationImages = NSArray(objects: imageOne, image2, image3, image4 ); refSequence.animationDuration = 1.4; refSequence.animationRepeatCount = 99; refSequence.startAnimating();
Am I doing something wrong? Is there some way to tint the images in the animation?
Thanks
How to solve the problem:
Solution 1:
Ok, i hoped that there is a simpler solution but this is what I ended up doing:
This function will create a new image with the wanted color:
func imageWithColor(img:UIImage, color:UIColor)->UIImage{ UIGraphicsBeginImageContextWithOptions(img.size, false, img.scale); var context = UIGraphicsGetCurrentContext(); CGContextTranslateCTM(context, 0, img.size.height); CGContextScaleCTM(context, 1.0, -1.0); CGContextSetBlendMode(context, kCGBlendModeNormal); var rect = CGRectMake(0, 0, img.size.width, img.size.height); CGContextClipToMask(context, rect, img.CGImage) color.setFill(); CGContextFillRect(context, rect); var newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newImage; }
And then you can call it like this:
var imageOne:UIImage = UIImage(named: "pullto_1.png")!; imageOne = imageWithColor(imageOne, color: UIColor.redColor()); var image2:UIImage = UIImage(named: "pullto_2.png")!; image2 = imageWithColor(image2, color: UIColor.redColor()); var image3:UIImage = UIImage(named: "pullto_3.png")!; image3 = imageWithColor(image3, color: UIColor.redColor()); var image4:UIImage = UIImage(named: "pullto_4.png")!; image4 = imageWithColor(image4, color: UIColor.redColor()); loaderS.animationImages = NSArray(objects: imageOne, image2, image3, image4 ); loaderS.animationDuration = 1.4; loaderS.animationRepeatCount = 99; loaderS.startAnimating();
Solution 2:
Here is a handy UIImage
extension:
import UIKit extension UIImage { func imageWithTint(tint: UIColor) -> UIImage { UIGraphicsBeginImageContextWithOptions(size, false, scale) let context = UIGraphicsGetCurrentContext() CGContextTranslateCTM(context, 0, size.height) CGContextScaleCTM(context, 1.0, -1.0) CGContextSetBlendMode(context, .Normal) let rect = CGRect(origin: .zero, size: size) CGContextClipToMask(context, rect, CGImage) tint.setFill() CGContextFillRect(context, rect) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image; } }
Solution 3:
There’s a rdar about this issue (http://www.openradar.me/23517334) and the problem still persists on iOS 11.
I adapted the code examples above to Swift 4.
extension UIImage { func image(withTintColor 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(origin: .zero, size: size) context?.clip(to: rect, mask: cgImage!) color.setFill() context?.fill(rect) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image! } }
Solution 4:
For Swift 5: Create image with color you want with below function. Then use those images to set to your image view’s animation property:
extension UIImage { func imageWithColor(_ color: UIColor) -> UIImage? { UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale); guard let context = UIGraphicsGetCurrentContext(), let cgImage = self.cgImage else { return nil } 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: size.width, height: size.height) context.clip(to: rect, mask: cgImage) color.setFill() context.fill(rect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext(); return newImage } } let animImages = [ image0.imageWithColor(color), image1.imageWithColor(color), image2.imageWithColor(color), ].compactMap({ $0 }) imageView.animationImages = animImages imageView.animationDuration = 0.7 imageView.animationRepeatCount = 0
Solution 5:
Here’s updated code for Swift 4 with a few safety checks.
extension UIImage { func image(withTint tint: UIColor) -> UIImage? { guard let cgImage = cgImage else { return nil } UIGraphicsBeginImageContextWithOptions(size, false, scale) guard let context = UIGraphicsGetCurrentContext() else { return nil } let rect = CGRect(origin: .zero, size: size) context.translateBy(x: 0, y: size.height) context.scaleBy(x: 1.0, y: -1.0) context.setBlendMode(.normal) context.clip(to: rect, mask: cgImage) tint.setFill() context.fill(rect) let image = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() return image } }
Solution 6:
Maybe this example extension helps:
extension UIImageView { func pulsingTintColor() { UIView.animate(withDuration: 2, delay: 0.0, options: [.repeat, .autoreverse], animations: { self.tintColor = UIColor.red self.tintColor = UIColor.green self.tintColor = UIColor.blue }, completion: nil) } }
Ensure you have set the Render as: Template Image option in your asset catalog. This works for UIViews as well. Just replace tintColor with backgroundColor.
If you need parametrised colours:
func pulsingTintColor(with colors: [UIColor] = [UIColor.red, UIColor.green, UIColor.blue]) { UIView.animate(withDuration: 2, delay: 0.0, options: [.repeat, .autoreverse], animations: { colors.forEach({self.tintColor = $0}) }, completion: nil) }