I stumbled in this answer searching for replacing a color with another.
Well, since that I need for Swift code, I have had several try and I find a nice solution. Thanks @Rob for its answer.
extension UIImageView {
@discardableResult func replace(color: UIColor, withColor replacingColor: UIColor) -> Bool {
guard let inputCGImage = self.image?.cgImage else {
return false
}
let colorSpace = CGColorSpaceCreateDeviceRGB()
let width = inputCGImage.width
let height = inputCGImage.height
let bytesPerPixel = 4
let bitsPerComponent = 8
let bytesPerRow = bytesPerPixel * width
let bitmapInfo = RGBA32.bitmapInfo
guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else {
print("unable to create context")
return false
}
context.draw(inputCGImage, in: CGRect(x: 0, y: 0, width: width, height: height))
guard let buffer = context.data else {
return false
}
let pixelBuffer = buffer.bindMemory(to: RGBA32.self, capacity: width * height)
let inColor = RGBA32(color: color)
let outColor = RGBA32(color: replacingColor)
for row in 0 ..< Int(height) {
for column in 0 ..< Int(width) {
let offset = row * width + column
if pixelBuffer[offset] == inColor {
pixelBuffer[offset] = outColor
}
}
}
guard let outputCGImage = context.makeImage() else {
return false
}
self.image = UIImage(cgImage: outputCGImage, scale: self.image!.scale, orientation: self.image!.imageOrientation)
return true
}
}
Where RGBA32 struct is simply:
struct RGBA32: Equatable {
private var color: UInt32
var redComponent: UInt8 {
return UInt8((self.color >> 24) & 255)
}
var greenComponent: UInt8 {
return UInt8((self.color >> 16) & 255)
}
var blueComponent: UInt8 {
return UInt8((self.color >> 8) & 255)
}
var alphaComponent: UInt8 {
return UInt8((self.color >> 0) & 255)
}
init(red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8) {
self.color = (UInt32(red) << 24) | (UInt32(green) << 16) | (UInt32(blue) << 8) | (UInt32(alpha) << 0)
}
init(color: UIColor) {
let components = color.cgColor.components ?? [0.0, 0.0, 0.0, 1.0]
let colors = components.map { UInt8($0 * 255) }
self.init(red: colors[0], green: colors[1], blue: colors[2], alpha: colors[3])
}
static let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
static func ==(lhs: RGBA32, rhs: RGBA32) -> Bool {
return lhs.color == rhs.color
}
}
In this way , wherever in your code:
let imageView = UIImageView()
let image = UIImage(color: .red) // (*)
imageView.image = image
imageView.replace(color: .red, withColor: .green)
(*) creating image with color is explained here.