検証環境:
Xcode 12.2
Swift 5.3.1
UIImage の画像を単色で塗りつぶす方法について。
元画像
変更後
UIImageView と一緒に使って変更する
UIImage を UIImageView にセットして一緒に使う場合は、UIImage のRenderingMode.alwaysTemplate
の指定とUIImageView のtintColor
の指定すれば実現できる。
let image = UIImage(named: "foo.png")! imageView.image = image.withRenderingMode(.alwaysTemplate) imageView.tintColor = .gray
UIImage 単体で変更する
UIImage 単体で変更したい場合は、ImageContext に元画像と単色画像をブレンドモードを使用して重ね合わせることで実現できる。
(例えばCALayer.contents
に直接セットするUIImage画像を単色にしたいケースなど)
以下2つどちらのやり方でも同様に変更できる。
CGBlendMode.destinationIn
を使う
func tintedImage(source: UIImage, color: UIColor) -> UIImage { UIGraphicsBeginImageContextWithOptions(source.size, false, source.scale) let rect = CGRect(origin: .zero, size: source.size) color.setFill() UIRectFill(rect) // 単色の背景画像として描画 source.draw(in: rect, blendMode: .destinationIn, alpha: 1) // 元画像を前景画像として描画 let resultImage = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() return resultImage }
CGBlendMode.destinationIn
を使う - UIGraphicsImageRenderer版 (iOS10以降)
func tintedImage(source: UIImage, color: UIColor) -> UIImage { return UIGraphicsImageRenderer(size: source.size).image { context in let rect = CGRect(origin: .zero, size: source.size) color.setFill() context.fill(rect) // 単色の背景画像として描画 source.draw(in: rect, blendMode: .destinationIn, alpha: 1) // 元画像を前景画像として描画 } }
CGBlendMode.sourceIn
を使う
func tintedImage(source: UIImage, color: UIColor) -> UIImage { UIGraphicsBeginImageContextWithOptions(source.size, false, source.scale) let context = UIGraphicsGetCurrentContext()! let rect = CGRect(origin: .zero, size: source.size) source.draw(in: rect) // 元画像を背景画像として描画 color.setFill() context.setBlendMode(.sourceIn) context.fill(rect) // 単色の前景画像として描画 let resultImage = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() return resultImage }
CGBlendMode.sourceIn
を使う - UIGraphicsImageRenderer版 (iOS10以降)
func tintedImage(source: UIImage, color: UIColor) -> UIImage { return UIGraphicsImageRenderer(size: source.size).image { context in let rect = CGRect(origin: .zero, size: source.size) source.draw(in: rect) // 元画像を背景画像として描画 color.setFill() context.fill(rect, blendMode: .sourceIn) // 単色の前景画像として描画 } }
- 追記: ImageContext 内で
RenderingMode.alwaysTemplate
を使う。
コードが一番短く書ける。
extension UIImage { func tinted(with color: UIColor) -> UIImage { return UIGraphicsImageRenderer(size: size).image { _ in color.set() withRenderingMode(.alwaysTemplate).draw(at: .zero) } } }
ブレンドモードについて
CGBlendMode
のAppleドキュメントはこちら。
上の例で使った2つのブレンドモードの方程式は
CGBlendMode.destinationIn
がR = D*Sa
CGBlendMode.sourceIn
がR = S*Da
とのこと。
R
は Result、S
は Source、D
が Destination、a
がアルファ値。
CGBlendMode.destinationIn
はDとSの重なっている部分だけ表示、色はSCGBlendMode.sourceIn
はSとDの重なっている部分だけ表示、色はD
になる。
以下のブレンド結果の画像例がわかりやすい。
元画像(赤の円)と混ぜ合わせる画像(青の正方形)をブレンドした例。
こちらの画像を引用。