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\"
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!
}
}
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)
}
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;
}
}
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
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
}
}
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();