はじめに
UIImageをリサイズしたいと思い、とりあえず「UIImage resize」とググってみたのですが、サンプルコードを動作させると思わぬ落とし穴にハマったので、備忘録として書き残しておきます。
画像が粗くなる(画像が劣化する)!?
だいたい検索すると出てくるサンプルコードはこんな感じです。(Swift3.0に書き直してます)
extension UIImage {
func resize(size _size: CGSize) -> UIImage? {
let widthRatio = _size.width / size.width
let heightRatio = _size.height / size.height
let ratio = widthRatio < heightRatio ? widthRatio : heightRatio
let resizedSize = CGSize(width: size.width * ratio, height: size.height * ratio)
UIGraphicsBeginImageContext(resizedSize)
draw(in: CGRect(origin: .zero, size: resizedSize))
let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resizedImage
}
}
これを実行すると、画像自体は希望のサイズにリサイズされたように見えますが、実際表示してみるとなんだか画像が粗い!?
原因はUIGraphicsBeginImageContext()
原因はUIGraphicsBeginImageContext(resizedSize)
でした。
これを用いて実行すると、Retinaディスプレイのscaleの設定が無視され、事実上のpixelサイズで扱われてリサイズ処理がされてしまいます。
つまり1xの画像として書き出されてしまうということでした。
これで解決
ということでUIGraphicsBeginImageContext(_ size: CGSize)
のかわりに、
UIGraphicsBeginImageContextWithOptions(_ size: CGSize, _ opaque: Bool, _ scale: CGFloat)
を使いましょう。
こちらを用いるとscaleが引数として設定されているので、Retinaディスプレイの場合も対象となるscaleを設定することで2x, 3xのような画像に対応することができます。
先ほどのコードを書き直してみます。
extension UIImage {
func resize(size _size: CGSize) -> UIImage? {
let widthRatio = _size.width / size.width
let heightRatio = _size.height / size.height
let ratio = widthRatio < heightRatio ? widthRatio : heightRatio
let resizedSize = CGSize(width: size.width * ratio, height: size.height * ratio)
UIGraphicsBeginImageContextWithOptions(resizedSize, false, 0.0) // 変更
draw(in: CGRect(origin: .zero, size: resizedSize))
let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resizedImage
}
}
UIGraphicsBeginImageContextWithOptions()の第二引数のopaqueは不透明かどうかなので、画像の不透明が保証されてる場合はtrueに指定した方がパフォーマンスがよくなるようです。
第三引数のscaleには0.0を入れておりますが、0.0を指定するとmainScreenのscaleを動的に設定してくれるので、4.7inchや5.5inchにてUIScreen.main.scale
をみて出し分ける必要はありません!
まとめ
UIImageのリサイズ処理を検索すると結構古い記事がヒットしてしまい、UIGraphicsBeginImageContext()
を使っている記事を参照しやすいので、現在の環境ではUIGraphicsBeginImageContextWithOptions()
を使っていきましょう。