Как узнать размер изображения после применения соотношения сторон изображения в UIImageView

Я загружаю изображение в изображение с режимом «Соотношение сторон». Мне нужно знать размер, до которого масштабируется мое изображение. Пожалуйста помоги.


person Nithin    schedule 08.06.2011    source источник


Ответы (19)


См. ответ @ Paul-de-Lange вместо этого


Я не смог найти ничего в легко доступной переменной, которая имела бы это, поэтому вот способ грубой силы:

- (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);

}
person Rayfleck    schedule 08.06.2011
comment
Этот код не точен. Он обрабатывает не все случаи. Например, если у вас есть изображение 2048 x 1536 и вы попытаетесь уместить его в 514 x 402. Он вернет масштабированный размер 402 (высота), когда он должен масштабировать его по ширине. Я изменил if ( a - x > b - y ) { на if ( a - x > b - y && (y/b * a) < x) {, чтобы исправить это. Их другие утверждения тоже следует проверить. - person renaun; 05.01.2014
comment
Здравствуйте, сэр, я нашел этот Anser полезным, но не могли бы вы помочь мне в моем вопросе: stackoverflow.com/questions/22727615/ - person Mrug; 29.03.2014

Почему бы не использовать функцию ОС AVMakeRectWithAspectRatioInsideRect?

person Paul de Lange    schedule 02.01.2013
comment
Это именно то, что нужно - [imageView setFrame:AVMakeRectWithAspectRatioInsideRect(image.size, imageView.frame)]) - person greg; 02.01.2013
comment
Хороший и чистый вариант. Вот ссылка на документы developer.apple. ru / library / ios / # documentation / AVFoundation / - person Popara; 29.07.2013
comment
Не хотите включать фреймворк AVFoundation? Я отправляю в AVMakeRectWithAspectRatioInsideRect() две альтернативные служебные функции в отдельном ответе. - person Timo; 30.07.2013
comment
Это должен быть принятый ответ. Принятый ответ выше неверен и не будет работать в разных случаях. - person jjpp; 23.08.2015
comment
Фантастика. Отлично. - person Akshit Zaveri; 14.11.2015
comment
Для быстрых: import AVFoundation let height = AVMakeRectWithAspectRatioInsideRect ((image? .Size) !, imageView.frame) .height - person Ben Sullivan; 21.06.2016

Я хотел использовать AVMakeRectWithAspectRatioInsideRect() без включения инфраструктуры AVFoundation.

Итак, я реализовал следующие две служебные функции:

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;
}

Изменить: оптимизировано ниже за счет удаления повторяющихся разделов.

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;
}

Конец редактирования

Он принимает заданный размер (первый параметр) и сохраняет его соотношение сторон. Затем он заполняет заданные границы (второй параметр) в максимально возможной степени без нарушения соотношения сторон.

Используя это, чтобы ответить на исходный вопрос:

// Using aspect fit, scale the image (size) to the image view's size.
CGSize sizeBeingScaledTo = CGSizeAspectFit(theImage.size, theImageView.frame.size);

Обратите внимание на то, как изображение определяет соотношение сторон, а представление изображения определяет размер заливки.

Обратная связь очень приветствуется.

person Timo    schedule 30.07.2013
comment
Это можно настроить для использования CGFloats, если вам нравятся подобные вещи. - person Timo; 30.07.2013
comment
Отличный ответ. Вы также можете настроить его для центрирования на определенном виде, чтобы получить фактический CGRect изображения внутри UIImageView с помощью AspectFit. Я сделал что-то вроде этого: ‹code› CGSize imageSize = CGSizeAspectFit (imageView.image.size, imageView.frame.size); CGRect actualImageRect = CGRectMake (0, 0, imageSize.width, imageSize.height); actualImageRect.origin.x = imageView.center.x - imageSize.width / 2; actualImageRect.origin.y = imageView.center.y - imageSize.height / 2; - person LilDwarf; 13.07.2014
comment
Спасибо, это отличная полезная функция. - person EralpB; 16.06.2015
comment
Я включил оптимизированную версию, которая удаляет повторяющиеся разделы. Теперь было бы интересно придумать аналог вне филиала. :) - person Timo; 08.12.2015

Эта простая функция рассчитает размер изображения после подгонки соотношения сторон:

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)
    }
}

Цель 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);

}
person Oleh Kudinov    schedule 24.01.2013
comment
Принятый ответ у меня не сработал. Но этот ответ сделал. И настоящая прелесть этого ответа в том, что ему не нужно передавать явную ссылку на UIImage, вместо этого он берет ее из содержимого изображения UIImageView. Работает и с изменением ориентации. Спасибо. - person Ayan Sengupta; 13.11.2013
comment
Поскольку это всегда делается в UIImageView, возможно, стоит преобразовать его в метод категории UIImageView. - person Timo; 01.09.2014
comment
он не возвращает точный кадр - person Aiyub Munshi; 08.07.2020

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
    }

}
person topLayoutGuide    schedule 09.05.2017
comment
Как насчет исходной позиции изображения вместо размера? - person karthikeyan; 12.01.2021

Я также хотел рассчитать высоту после применения соотношения сторон, чтобы иметь возможность рассчитать высоту ячейки табличного представления. Итак, я достиг с помощью небольшой математики

ratio = width / height

и рост стал бы

height = width / ratio

Таким образом, фрагмент кода будет

UIImage *img = [UIImage imageNamed:@"anImage"];
float aspectRatio = img.size.width/img.size.height;
float requiredHeight = self.view.bounds.size.width / aspectRatio;
person zeeawan    schedule 30.10.2015
comment
Элегантный. Я использую его внутри класса UITableViewCell, поэтому requiredHeight становится float requiredHeight = self.bounds.size.width / aspectRatio;. - person oyalhi; 20.05.2016
comment
Вы молодец! Это должен был быть принятый ответ! - person Marian Petrisor; 19.09.2018

Для 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)
    }

И функция вызова

imgFeed.sd_setImageWithURL(NSURL(string:"Your image URL")))
self.imageSizeAfterAspectFit(imgFeed)
person Hardik Thakkar    schedule 18.06.2016

Возможно, это не подходит для вашего случая, но этот простой подход решает мою проблему в аналогичном случае:

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

После того, как просмотр изображения выполнит sizeToFit, если вы запросите imageView.frame.size, вы получите новый размер представления изображения, который соответствует размеру нового изображения.

person PakitoV    schedule 13.04.2013

Swift 4: рамка для .aspectFit изображения -

import AVFoundation

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

person Jack    schedule 29.11.2017
comment
да, я сделал, но у меня нет места на y, но он возвращается как 40 - person Kishore Kumar; 28.06.2018
comment
Это неожиданно, пробовали ли вы использовать новый контроллер представления с одним imageView без ограничений? - person Jack; 28.06.2018
comment
Я пробовал использовать uiview xib, в этом представлении изображения было, значение origin x равно y, а значение orgin y равно x. и я использую ограничения. - person Kishore Kumar; 28.06.2018
comment
как вы сказали, это проблема из-за ограничений @Jack - person Kishore Kumar; 28.06.2018

Расширение 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
  }  
}
person Paul King    schedule 24.05.2017

Эта единственная строка может сделать эту работу

CGSize sizeInView = AVMakeRectWithAspectRatioInsideRect(imgViewFake.image.size, imgViewFake.bounds).size;
person Darshit Shah    schedule 30.03.2017

Принятый ответ невероятно сложен и не подходит для некоторых крайних случаев. Думаю, это решение намного элегантнее:

- (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);
}
person blkhp19    schedule 01.08.2014

Вот мое решение той же проблемы: https://github.com/alexgarbarev/UIImageView-ImageFrame

Преимущества:

  • Поддерживаемые режимы UIViewContentMode
  • Может запрашивать шкалы и прямоугольник отдельно
  • Можно спросить о кадре изображения прямо из UIImageView
person Aleksey    schedule 16.01.2015

Вот мое решение без AVFoundation.

Во-первых, это расширение 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)
    }
}

Итак, решение проблемы OP сводится к:

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

Также вот еще одно расширение для размещения прямоугольника внутри другого прямоугольника (оно использует указанное выше расширение 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
    }
}
person Russian    schedule 20.06.2017

В 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
}

Я использую это так:

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)
person JollyJinx    schedule 14.04.2016

Если вам известна только ширина изображения и когда высота изображения является динамической, вам необходимо масштабировать высоту изображения в соответствии с заданной шириной, чтобы удалить пробелы над и под изображением. Используйте следующий метод из здесь, чтобы масштабировать высоту изображения в соответствии со стандартной шириной экрана.

-(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;
}

И вызовите его из своего метода cellForRowAtIndexPath: следующим образом:

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

Версия 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))
    }
}
person fewlinesofcode    schedule 24.09.2018

Вышеупомянутые методы никогда не дают требуемых значений. Так как соотношение сторон сохраняется, нам просто нужны простые математические вычисления для вычисления значений.

Определить соотношение сторон

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

Получите новые ценности

CGFloat newImageWidth = mulFactor * backgroundImageView.bounds.size.width;
CGFloat newImageHeight = mulFactor * backgroundImageView.bounds.size.height;
person Suryanarayan Sahu    schedule 18.05.2016

Swift 5 Extension

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;
    }
}
person Li Jin    schedule 02.04.2021