はじめに
こんにちは
UIImageViewにはcontentMode
という表示モードがあります。
参考記事: UIImageView 表示モード一覧 siro:chro
その時のUIImageが表示されている矩形を頑張って計算してみたので、共有したいと思います。Playground等を利用して複数確認をしましたが、至らぬ点などコメント頂けたら幸いです。
使い所
利用する頻度は低いかもしれないのですが、UIImageViewのcontentModeをscaleAspectFit
やscaleAspectFill
などに設定した時の画像のframe
やbounds
を取得することができます。
ソースコード
UIImageViewの全てのcontentModeに対応させましたが、比較的よく利用するscaleAspectFit
やscaleAspectFill
だけで良いという方は一部参考にして頂ければと思います。
import UIKit
extension UIImageView {
private var aspectFitSize: CGSize? {
get {
guard let aspectRatio = image?.size else { return nil }
let widthRatio = bounds.width / aspectRatio.width
let heightRatio = bounds.height / aspectRatio.height
let ratio = (widthRatio > heightRatio) ? heightRatio : widthRatio
let resizedWidth = aspectRatio.width * ratio
let resizedHeight = aspectRatio.height * ratio
let aspectFitSize = CGSize(width: resizedWidth, height: resizedHeight)
return aspectFitSize
}
}
var aspectFitFrame: CGRect? {
get {
guard let size = aspectFitSize else { return nil }
return CGRect(origin: CGPoint(x: frame.origin.x + (bounds.size.width - size.width) * 0.5, y: frame.origin.y + (bounds.size.height - size.height) * 0.5), size: size)
}
}
var aspectFitBounds: CGRect? {
get {
guard let size = aspectFitSize else { return nil }
return CGRect(origin: CGPoint(x: bounds.size.width * 0.5 - size.width * 0.5, y: bounds.size.height * 0.5 - size.height * 0.5), size: size)
}
}
private var aspectFillSize: CGSize? {
get {
guard let aspectRatio = image?.size else { return nil }
let widthRatio = bounds.width / aspectRatio.width
let heightRatio = bounds.height / aspectRatio.height
let ratio = (widthRatio < heightRatio) ? heightRatio : widthRatio
let resizedWidth = aspectRatio.width * ratio
let resizedHeight = aspectRatio.height * ratio
let aspectFitSize = CGSize(width: resizedWidth, height: resizedHeight)
return aspectFitSize
}
}
var aspectFillFrame: CGRect? {
get {
guard let size = aspectFillSize else { return nil }
return CGRect(origin: CGPoint(x: frame.origin.x - (size.width - bounds.size.width) * 0.5, y: frame.origin.y - (size.height - bounds.size.height) * 0.5), size: size)
}
}
var aspectFillBounds: CGRect? {
get {
guard let size = aspectFillSize else { return nil }
return CGRect(origin: CGPoint(x: bounds.origin.x - (size.width - bounds.size.width) * 0.5, y: bounds.origin.y - (size.height - bounds.size.height) * 0.5), size: size)
}
}
func imageFrame(_ contentMode: UIViewContentMode) -> CGRect? {
guard let image = image else { return nil }
switch contentMode {
case .scaleToFill, .redraw:
return frame
case .scaleAspectFit:
return aspectFitFrame
case .scaleAspectFill:
return aspectFillFrame
case .center:
let x = frame.origin.x - (image.size.width - bounds.size.width) * 0.5
let y = frame.origin.y - (image.size.height - bounds.size.height) * 0.5
return CGRect(origin: CGPoint(x: x, y: y), size: image.size)
case .topLeft:
return CGRect(origin: frame.origin, size: image.size)
case .top:
let x = frame.origin.x - (image.size.width - bounds.size.width) * 0.5
let y = frame.origin.y
return CGRect(origin: CGPoint(x: x, y: y), size: image.size)
case .topRight:
let x = frame.origin.x - (image.size.width - bounds.size.width)
let y = frame.origin.y
return CGRect(origin: CGPoint(x: x, y: y), size: image.size)
case .right:
let x = frame.origin.x - (image.size.width - bounds.size.width)
let y = frame.origin.y - (image.size.height - bounds.size.height) * 0.5
return CGRect(origin: CGPoint(x: x, y: y), size: image.size)
case .bottomRight:
let x = frame.origin.x - (image.size.width - bounds.size.width)
let y = frame.origin.y + (bounds.size.height - image.size.height)
return CGRect(origin: CGPoint(x: x, y: y), size: image.size)
case .bottom:
let x = frame.origin.x - (image.size.width - bounds.size.width) * 0.5
let y = frame.origin.y + (bounds.size.height - image.size.height)
return CGRect(origin: CGPoint(x: x, y: y), size: image.size)
case .bottomLeft:
let x = frame.origin.x
let y = frame.origin.y + (bounds.size.height - image.size.height)
return CGRect(origin: CGPoint(x: x, y: y), size: image.size)
case .left:
let x = frame.origin.x
let y = frame.origin.y - (image.size.height - bounds.size.height) * 0.5
return CGRect(origin: CGPoint(x: x, y: y), size: image.size)
}
}
func imageBounds(_ contentMode: UIViewContentMode) -> CGRect? {
guard let image = image else { return nil }
switch contentMode {
case .scaleToFill, .redraw:
return bounds
case .scaleAspectFit:
return aspectFitBounds
case .scaleAspectFill:
return aspectFillBounds
case .center:
let x = bounds.size.width * 0.5 - image.size.width * 0.5
let y = bounds.size.height * 0.5 - image.size.height * 0.5
return CGRect(origin: CGPoint(x: x, y: y), size: image.size)
case .topLeft:
return CGRect(origin: CGPoint.zero, size: image.size)
case .top:
let x = bounds.size.width * 0.5 - image.size.width * 0.5
let y: CGFloat = 0
return CGRect(origin: CGPoint(x: x, y: y), size: image.size)
case .topRight:
let x = bounds.size.width - image.size.width
let y: CGFloat = 0
return CGRect(origin: CGPoint(x: x, y: y), size: image.size)
case .right:
let x = bounds.size.width - image.size.width
let y = bounds.size.height * 0.5 - image.size.height * 0.5
return CGRect(origin: CGPoint(x: x, y: y), size: image.size)
case .bottomRight:
let x = bounds.size.width - image.size.width
let y = bounds.size.height - image.size.height
return CGRect(origin: CGPoint(x: x, y: y), size: image.size)
case .bottom:
let x = bounds.size.width * 0.5 - image.size.width * 0.5
let y = bounds.size.height - image.size.height
return CGRect(origin: CGPoint(x: x, y: y), size: image.size)
case .bottomLeft:
let x: CGFloat = 0
let y = bounds.size.height - image.size.height
return CGRect(origin: CGPoint(x: x, y: y), size: image.size)
case .left:
let x: CGFloat = 0
let y = bounds.size.height * 0.5 - image.size.height * 0.5
return CGRect(origin: CGPoint(x: x, y: y), size: image.size)
}
}
}
実装について
上記のaspectFitBounds
は計算しなくてもAVFoundation
のAVMakeRect(aspectRatio:insideRect:)
で取得することもできます。
var aspectFitBounds: CGRect? {
get {
guard let image = image else { return nil }
return AVMakeRect(aspectRatio: image.size, insideRect: bounds)
}
}
おわりに
PlaygroundのPlaygroundSupportを利用しての
UIViewを表示させてUIの確認ができる機能がとても使いやすかったです。
この様な矩形計算等はObjective-C時代から私よりも良い書き方で優良な記事がたくさんあると思うのですが、Swift3時代のExtensionで書けたことと、UIImageViewのcontentModeの理解を深められたのでよかったと思います。
参考にさせていただいた記事
見て頂いてありがとうございます。