简体   繁体   English

在 UIImageView 中应用适合图像的宽高比后如何知道图像大小

[英]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'.我正在将图像加载到 imageview,模式为“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 ?为什么不使用操作系统 function AVMakeRectWithAspectRatioInsideRect

I wanted to use AVMakeRectWithAspectRatioInsideRect() without including the AVFoundation framework .我想使用AVMakeRectWithAspectRatioInsideRect()而不包括 AVFoundation 框架

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请参阅@Paul-de-Lange 的答案而不是这个答案


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:这个简单的 function 将在方面适合后计算图像的大小:

Swift 5.1 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:目标 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 Swift 3人类可读版本

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
    }

}

I also wanted to calculate height after the aspect ratio is applied to be able to calculate the height of table view's cell.我还想在应用纵横比之后计算高度,以便能够计算表格视图单元格的高度。 So, I achieved via little math所以,我通过小数学实现了

ratio = width / height

and height would become高度会变成

height = width / ratio

So code snippet would be所以代码片段将是

UIImage *img = [UIImage imageNamed:@"anImage"];
float aspectRatio = img.size.width/img.size.height;
float requiredHeight = self.view.bounds.size.width / aspectRatio;

For Swift use below code对于 Swift 使用下面的代码

func imageSizeAspectFit(imgview: UIImageView) -> CGSize {
        var newwidth: CGFloat
        var newheight: CGFloat
        let image: UIImage = imgFeed.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 {
                let diff: CGFloat = 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 {
                let diff: CGFloat = imgview.frame.size.height - newheight
                newwidth = newwidth + diff / newwidth * newwidth
                newheight = imgview.frame.size.height
            }
        }

       print(newwidth, newheight)
        //adapt UIImageView size to image size
        return CGSizeMake(newwidth, newheight)
    }

And Call Function并致电 Function

imgFeed.sd_setImageWithURL(NSURL(string:"Your image URL")))
self.imageSizeAfterAspectFit(imgFeed)

Maybe does not fit your case, but this simple approach solve my problem in a similar case:也许不适合您的情况,但是这种简单的方法在类似情况下解决了我的问题:

    UIImageView *imageView = [[UIImageView alloc] initWithImage:bigSizeImage];
    [imageView sizeToFit];

After image view executes sizeToFit if you query the imageView.frame.size you will get the new image view size that fits the new image size.在图像视图执行 sizeToFit 后,如果您查询imageView.frame.size ,您将获得适合新图像大小的新图像视图大小。

+(UIImage *)CreateAResizeImage:(UIImage *)Img ThumbSize:(CGSize)ThumbSize 
{
    float actualHeight = Img.size.height;
    float actualWidth = Img.size.width;

    if(actualWidth==actualHeight) 
    {
        actualWidth = ThumbSize.width; 
        actualHeight = ThumbSize.height;
    }

    float imgRatio = actualWidth/actualHeight;
    float maxRatio = ThumbSize.width/ThumbSize.height; //320.0/480.0;


    if(imgRatio!=maxRatio)
    {
        if(imgRatio < maxRatio)
        {
            imgRatio = ThumbSize.height / actualHeight; //480.0 / actualHeight;
            actualWidth = imgRatio * actualWidth;
            actualHeight = ThumbSize.height; //480.0;
        }
        else
        {
            imgRatio = ThumbSize.width / actualWidth; //320.0 / actualWidth;
            actualHeight = imgRatio * actualHeight;
            actualWidth = ThumbSize.width; //320.0;
        }
    } 
    else 
    {
        actualWidth = ThumbSize.width;
        actualHeight = ThumbSize.height; 
    }


    CGRect rect = CGRectMake(0, 0, (int)actualWidth, (int)actualHeight);
    UIGraphicsBeginImageContext(rect.size);
    [Img drawInRect:rect];
    UIImage *NewImg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return NewImg;
}

Swift 4: Frame for .aspectFit image is - Swift 4: .aspectFit图像的框架是 -

import AVFoundation

let x: CGRect = AVMakeRect(aspectRatio: myImage.size, insideRect: sampleImageView.frame)

This single line can do this job这条单线可以完成这项工作

CGSize sizeInView = AVMakeRectWithAspectRatioInsideRect(imgViewFake.image.size, imgViewFake.bounds).size;

Swift 3 UIImageView extension: Swift 3 UIImageView 扩展:

import AVFoundation

extension UIImageView {
  var imageSize: CGSize {
    if let image = image {
      return AVMakeRect(aspectRatio: image.size, insideRect: bounds).size
    }
    return CGSize.zero
  }  
}

The accepted answer is incredibly complicated and fails for some edge cases.接受的答案非常复杂,并且在某些极端情况下会失败。 I think this solution is much more elegant:我认为这个解决方案更加优雅:

- (CGSize) sizeOfImage:(UIImage*)image inAspectFitImageView:(UIImageView*)imageView
{
    UKAssert(imageView.contentMode == UIViewContentModeScaleAspectFit, @"Image View must use contentMode = UIViewContentModeScaleAspectFit");

    CGFloat imageViewWidth = imageView.bounds.size.width;
    CGFloat imageViewHeight = imageView.bounds.size.height;

    CGFloat imageWidth = image.size.width;
    CGFloat imageHeight = image.size.height;

    CGFloat scaleFactor = MIN(imageViewWidth / imageWidth, imageViewHeight / imageHeight);

    return CGSizeMake(image.size.width*scaleFactor, image.size.height*scaleFactor);
}

Here is my solution for same problem: https://github.com/alexgarbarev/UIImageView-ImageFrame这是我针对相同问题的解决方案: https://github.com/alexgarbarev/UIImageView-ImageFrame

Advantages:优点:

  • UIViewContentMode modes supported支持 UIViewContentMode 模式
  • Can query for scales and for rect separately可以分别查询比例和矩形
  • Can ask about image frame right from UIImageView可以从 UIImageView 询问图像框架

Here's my AVFoundation-less solution.这是我的无 AVFoundation 解决方案。

First here's a CGSize extension for calculating a size that would fit another size:首先,这是一个 CGSize 扩展,用于计算适合另一个尺寸的尺寸:

extension CGSize
{
    func sizeThatFitsSize(_ aSize: CGSize) -> CGSize
    {
        let width = min(self.width * aSize.height / self.height, aSize.width)
        return CGSize(width: width, height: self.height * width / self.width)
    }
}

So the solution to OP's problem gets down to:因此,OP问题的解决方案归结为:

let resultSize = image.size.sizeThatFitsSize(imageView.bounds.size)

Also here's another extension for fitting a rect within another rect (it utilizes the above CGSize extension):这里还有另一个用于在另一个矩形中拟合矩形的扩展(它利用上面的 CGSize 扩展):

extension CGRect
{
    func rectThatFitsRect(_ aRect:CGRect) -> CGRect
    {
        let sizeThatFits = self.size.sizeThatFitsSize(aRect.size)

        let xPos = (aRect.size.width - sizeThatFits.width) / 2
        let yPos = (aRect.size.height - sizeThatFits.height) / 2

        let ret = CGRect(x: xPos, y: yPos, width: sizeThatFits.width, height: sizeThatFits.height)
        return ret
    }
}

I'm using the following in Swift:我在 Swift 中使用以下内容:

private func CGSizeAspectFit(aspectRatio:CGSize,boundingSize:CGSize) -> CGSize
{
    var aspectFitSize = boundingSize
    let mW = boundingSize.width / aspectRatio.width
    let mH = boundingSize.height / aspectRatio.height
    if( mH < mW )
    {
        aspectFitSize.width = mH * aspectRatio.width
    }
    else if( mW < mH )
    {
        aspectFitSize.height = mW * aspectRatio.height
    }
    return aspectFitSize
}

private func CGSizeAspectFill(aspectRatio:CGSize,minimumSize:CGSize) -> CGSize
{
    var aspectFillSize = minimumSize
    let mW = minimumSize.width / aspectRatio.width
    let mH = minimumSize.height / aspectRatio.height
    if( mH > mW )
    {
        aspectFillSize.width = mH * aspectRatio.width
    }
    else if( mW > mH )
    {
        aspectFillSize.height = mW * aspectRatio.height
    }
    return aspectFillSize
}

I'm using it like this:我这样使用它:

let aspectSize  = contentMode == .ScaleAspectFill ? CGSizeAspectFill(oldSize,minimumSize: newSize) : CGSizeAspectFit(oldSize, boundingSize: newSize)

let newRect = CGRect( x: (newSize.width - aspectSize.width)/2, y: (newSize.height - aspectSize.height)/2, width: aspectSize.width, height: aspectSize.height)

CGContextSetFillColorWithColor(context,IOSXColor.whiteColor().CGColor)
CGContextFillRect(context, CGRect(origin: CGPointZero,size: newSize))
CGContextDrawImage(context, newRect, cgImage)

If you know only the width of the imageview and when the height of the image is dynamic then you need to scale the image's height according to the given width to remove the white spaces above and below your image.如果您只知道 imageview 的宽度并且图像的高度是动态的,那么您需要根据给定的宽度缩放图像的高度,以删除图像上方和下方的空白。 Use the following method from here to scale the height of the image according to the standard width of your screen.这里使用以下方法根据屏幕的标准宽度缩放图像的高度。

-(UIImage*)imageWithImage: (UIImage*) sourceImage scaledToWidth: (float) i_width
{
    float oldWidth = sourceImage.size.width;
    float scaleFactor = i_width / oldWidth;

    float newHeight = sourceImage.size.height * scaleFactor;
    float newWidth = oldWidth * scaleFactor;

    UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight));
    [sourceImage drawInRect:CGRectMake(0, 0, newWidth, newHeight)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
    UIGraphicsEndImageContext();
    return newImage;
}

And call it from your cellForRowAtIndexPath: method like this:并从您的 cellForRowAtIndexPath: 方法中调用它,如下所示:

UIImage *img = [dictImages objectForKey:yourImageKey]; // loaded the image
cell.imgView.image = [self imageWithImage:img scaledToWidth:self.view.frame.size.width];

Swift 4 version Swift 4版

extension CGSize {
   enum AspectMode {
       case fit
       case fill
   }

   enum Orientation {
       case portrait
       case landscape
   }

   func aspectCorrectSizeToFit(targetSize: CGSize, aspectMode: AspectMode = .fill) -> CGSize {
        switch aspectMode {
        case .fill: return aspectFill(targetSize: targetSize)
        case .fit: return aspectFit(targetSize: targetSize)
        }
    }

    var orientation: Orientation {
        if height >= width { return .portrait }
        else { return .landscape }
    }

    func aspectFit(targetSize: CGSize) -> CGSize {
        let wRatio = targetSize.width / width
        let hRatio = targetSize.height / height
        let scale = min(wRatio, hRatio)
        return applying(CGAffineTransform(scaleX: scale, y: scale))
    }

    func aspectFill(targetSize: CGSize) -> CGSize {
        let wRatio = targetSize.width / width
        let hRatio = targetSize.height / height
        let scale = max(wRatio, hRatio)
        return applying(CGAffineTransform(scaleX: scale, y: scale))
    }
}

Swift 5 Extension Swift 5扩展

extension CGSize {
    func aspectFit(to size: CGSize) -> CGSize {
        let mW = size.width / self.width;
        let mH = size.height / self.height;
        
        var result = size
        if( mH < mW ) {
            result.width = size.height / self.height * self.width;
        }
        else if( mW < mH ) {
            result.height = size.width / self.width * self.height;
        }
        
        return result;
    }
    
    func aspectFill(to size: CGSize) -> CGSize {
        let mW = size.width / self.width;
        let mH = size.height / self.height;
        
        var result = size
        if( mH > mW ) {
            result.width = size.height / self.height * self.width;
        }
        else if( mW > mH ) {
            result.height = size.width / self.width * self.height;
        }
        return result;
    }
}

The above mentioned methods never give the required values.As aspect fit maintains the same aspect ratio we just need simple maths to calculate the values上述方法从未给出所需的值。由于纵横比保持相同的纵横比,我们只需要简单的数学来计算值

Detect the aspect ratio检测纵横比

CGFloat imageViewAspectRatio = backgroundImageView.bounds.size.width / backgroundImageView.bounds.size.height;
CGFloat imageAspectRatio =  backgroundImageView.image.size.width / backgroundImageView.image.size.height;
CGFloat mulFactor = imageViewAspectRatio/imageAspectRatio;

Get the new values获取新值

CGFloat newImageWidth = mulFactor * backgroundImageView.bounds.size.width;
CGFloat newImageHeight = mulFactor * backgroundImageView.bounds.size.height;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM