How to know the image size after applying aspect fit for the image in an UIImageView
I am loading an image to an imageview with mode as 'Aspect Fit'. I need to know the size to which my image is being scaled to. Please help.
Why not use the OS function AVMakeRectWithAspectRatioInsideRect?
I wanted to use AVMakeRectWithAspectRatioInsideRect()
without including the AVFoundation framework.
So I've implemented the following two utility functions:
CGSize CGSizeAspectFit(CGSize aspectRatio, CGSize boundingSize)
{
float mW = boundingSize.width / aspectRatio.width;
float mH = boundingSize.height / aspectRatio.height;
if( mH < mW )
boundingSize.width = boundingSize.height / aspectRatio.height * aspectRatio.width;
else if( mW < mH )
boundingSize.height = boundingSize.width / aspectRatio.width * aspectRatio.height;
return boundingSize;
}
CGSize CGSizeAspectFill(CGSize aspectRatio, CGSize minimumSize)
{
float mW = minimumSize.width / aspectRatio.width;
float mH = minimumSize.height / aspectRatio.height;
if( mH > mW )
minimumSize.width = minimumSize.height / aspectRatio.height * aspectRatio.width;
else if( mW > mH )
minimumSize.height = minimumSize.width / aspectRatio.width * aspectRatio.height;
return minimumSize;
}
Edit: Optimized below by removing duplicate divisions.
CGSize CGSizeAspectFit(const CGSize aspectRatio, const CGSize boundingSize)
{
CGSize aspectFitSize = CGSizeMake(boundingSize.width, boundingSize.height);
float mW = boundingSize.width / aspectRatio.width;
float mH = boundingSize.height / aspectRatio.height;
if( mH < mW )
aspectFitSize.width = mH * aspectRatio.width;
else if( mW < mH )
aspectFitSize.height = mW * aspectRatio.height;
return aspectFitSize;
}
CGSize CGSizeAspectFill(const CGSize aspectRatio, const CGSize minimumSize)
{
CGSize aspectFillSize = CGSizeMake(minimumSize.width, minimumSize.height);
float mW = minimumSize.width / aspectRatio.width;
float mH = minimumSize.height / aspectRatio.height;
if( mH > mW )
aspectFillSize.width = mH * aspectRatio.width;
else if( mW > mH )
aspectFillSize.height = mW * aspectRatio.height;
return aspectFillSize;
}
End of edit
This takes a given size (first parameter) and maintains its aspect ratio. It then fills the given bounds (second parameter) as much as possible without violating the aspect ratio.
Using this to answer the original question:
// Using aspect fit, scale the image (size) to the image view's size.
CGSize sizeBeingScaledTo = CGSizeAspectFit(theImage.size, theImageView.frame.size);
Note how the image determines the aspect ratio, while the image view determines the size to be filled.
Feedback is very welcome.
Please see @Paul-de-Lange's answer instead of this one
I couldn't find anything in an easily accessible variable that had this, so here is the brute force way:
- (CGSize) aspectScaledImageSizeForImageView:(UIImageView *)iv image:(UIImage *)im {
float x,y;
float a,b;
x = iv.frame.size.width;
y = iv.frame.size.height;
a = im.size.width;
b = im.size.height;
if ( x == a && y == b ) { // image fits exactly, no scaling required
// return iv.frame.size;
}
else if ( x > a && y > b ) { // image fits completely within the imageview frame
if ( x-a > y-b ) { // image height is limiting factor, scale by height
a = y/b * a;
b = y;
} else {
b = x/a * b; // image width is limiting factor, scale by width
a = x;
}
}
else if ( x < a && y < b ) { // image is wider and taller than image view
if ( a - x > b - y ) { // height is limiting factor, scale by height
a = y/b * a;
b = y;
} else { // width is limiting factor, scale by width
b = x/a * b;
a = x;
}
}
else if ( x < a && y > b ) { // image is wider than view, scale by width
b = x/a * b;
a = x;
}
else if ( x > a && y < b ) { // image is taller than view, scale by height
a = y/b * a;
b = y;
}
else if ( x == a ) {
a = y/b * a;
b = y;
} else if ( y == b ) {
b = x/a * b;
a = x;
}
return CGSizeMake(a,b);
}
This simple function will calculate size of image after aspect fit:
Swift 5.1
extension UIImageView {
var imageSizeAfterAspectFit: CGSize {
var newWidth: CGFloat
var newHeight: CGFloat
guard let image = image else { return frame.size }
if image.size.height >= image.size.width {
newHeight = frame.size.height
newWidth = ((image.size.width / (image.size.height)) * newHeight)
if CGFloat(newWidth) > (frame.size.width) {
let diff = (frame.size.width) - newWidth
newHeight = newHeight + CGFloat(diff) / newHeight * newHeight
newWidth = frame.size.width
}
} else {
newWidth = frame.size.width
newHeight = (image.size.height / image.size.width) * newWidth
if newHeight > frame.size.height {
let diff = Float((frame.size.height) - newHeight)
newWidth = newWidth + CGFloat(diff) / newWidth * newWidth
newHeight = frame.size.height
}
}
return .init(width: newWidth, height: newHeight)
}
}
Objective C:
-(CGSize)imageSizeAfterAspectFit:(UIImageView*)imgview{
float newwidth;
float newheight;
UIImage *image=imgview.image;
if (image.size.height>=image.size.width){
newheight=imgview.frame.size.height;
newwidth=(image.size.width/image.size.height)*newheight;
if(newwidth>imgview.frame.size.width){
float diff=imgview.frame.size.width-newwidth;
newheight=newheight+diff/newheight*newheight;
newwidth=imgview.frame.size.width;
}
}
else{
newwidth=imgview.frame.size.width;
newheight=(image.size.height/image.size.width)*newwidth;
if(newheight>imgview.frame.size.height){
float diff=imgview.frame.size.height-newheight;
newwidth=newwidth+diff/newwidth*newwidth;
newheight=imgview.frame.size.height;
}
}
NSLog(@"image after aspect fit: width=%f height=%f",newwidth,newheight);
//adapt UIImageView size to image size
//imgview.frame=CGRectMake(imgview.frame.origin.x+(imgview.frame.size.width-newwidth)/2,imgview.frame.origin.y+(imgview.frame.size.height-newheight)/2,newwidth,newheight);
return CGSizeMake(newwidth, newheight);
}
Swift 3 Human Readable Version
extension UIImageView {
/// Find the size of the image, once the parent imageView has been given a contentMode of .scaleAspectFit
/// Querying the image.size returns the non-scaled size. This helper property is needed for accurate results.
var aspectFitSize: CGSize {
guard let image = image else { return CGSize.zero }
var aspectFitSize = CGSize(width: frame.size.width, height: frame.size.height)
let newWidth: CGFloat = frame.size.width / image.size.width
let newHeight: CGFloat = frame.size.height / image.size.height
if newHeight < newWidth {
aspectFitSize.width = newHeight * image.size.width
} else if newWidth < newHeight {
aspectFitSize.height = newWidth * image.size.height
}
return aspectFitSize
}
/// Find the size of the image, once the parent imageView has been given a contentMode of .scaleAspectFill
/// Querying the image.size returns the non-scaled, vastly too large size. This helper property is needed for accurate results.
var aspectFillSize: CGSize {
guard let image = image else { return CGSize.zero }
var aspectFillSize = CGSize(width: frame.size.width, height: frame.size.height)
let newWidth: CGFloat = frame.size.width / image.size.width
let newHeight: CGFloat = frame.size.height / image.size.height
if newHeight > newWidth {
aspectFillSize.width = newHeight * image.size.width
} else if newWidth > newHeight {
aspectFillSize.height = newWidth * image.size.height
}
return aspectFillSize
}
}