Cambiar el tamaño de UIImage manteniendo la relación de aspecto y el ancho

136

Vi en muchas publicaciones para cambiar el tamaño de la imagen manteniendo la relación de aspecto. Estas funciones utilizan los puntos fijos (Ancho y Altura) para RECT mientras cambia el tamaño. Pero en mi proyecto, necesito cambiar el tamaño de la vista basándome solo en el Ancho, la Altura debe tomarse automáticamente según la relación de aspecto. Alguien me ayude a lograr esto.

Jazmín
fuente

Respuestas:

228

El método de Srikar funciona muy bien, si conoce tanto la altura como el ancho de su nuevo Tamaño. Si, por ejemplo, solo conoce el ancho al que desea escalar y no le importa la altura, primero debe calcular el factor de escala de la altura.

+(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;
}
Maverick1st
fuente
22
Realmente no necesitas la newWidthvariable aquí, ya lo es i_width.
Matthew Quiros
2
También debe dividir por altura y comparar los factores de escala utilizando el que esté más cerca de 0. El código anterior solo funcionará con imágenes que sean más anchas que altas
IceForge
1
Solo necesitaría dividir por altura si quisiera saber también mi relación de altura y ajustar escalando el ancho o la altura. pero dado que el TO solicitó explícitamente mantener la proporción y el ancho, y no el ancho o la altura, mi respuesta es completamente correcta.
Maverick1st
12
Gran respuesta, gracias. Una cosa más: si experimenta alguna pérdida de calidad de imagen con este método, use en UIGraphicsBeginImageContextWithOptions(CGSizeMake(newWidth, newHeight), NO, 0);lugar deUIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight));
skornos
57

Si no sabe si la imagen será vertical u horizontal (por ejemplo, el usuario toma una foto con la cámara), creé otro método que toma parámetros de ancho y altura máximos.

Digamos que tiene una UIImage *myLargeImagerelación 4: 3.

UIImage *myResizedImage = [ImageUtilities imageWithImage:myLargeImage 
                                        scaledToMaxWidth:1024 
                                               maxHeight:1024];

El UIImage redimensionado será 1024x768 si es horizontal; 768x1024 si retrato. Este método también generará imágenes de mayor resolución para la visualización de retina.

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)size {
    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
        UIGraphicsBeginImageContextWithOptions(size, NO, [[UIScreen mainScreen] scale]);
    } else {
        UIGraphicsBeginImageContext(size);
    }
    [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
    UIGraphicsEndImageContext();

    return newImage;
}

+ (UIImage *)imageWithImage:(UIImage *)image scaledToMaxWidth:(CGFloat)width maxHeight:(CGFloat)height {
    CGFloat oldWidth = image.size.width;
    CGFloat oldHeight = image.size.height;

    CGFloat scaleFactor = (oldWidth > oldHeight) ? width / oldWidth : height / oldHeight;

    CGFloat newHeight = oldHeight * scaleFactor;
    CGFloat newWidth = oldWidth * scaleFactor;
    CGSize newSize = CGSizeMake(newWidth, newHeight);

    return [ImageUtilities imageWithImage:image scaledToSize:newSize];
}
Ryan
fuente
1
Esto es lo mejor, pero siempre devuelve el doble de mi tamaño definido
Mashhadi
2
Si solo desea reducir el tamaño, no aumentar el tamaño, no olvide iniciar imageWithImage: scaledToMaxWidth: maxHeight: método con: if (image.size.width <width && image.size.height <height) {return image; }
Arjan
55
Para caber dentro de un ancho máximo y la altura máxima, es necesario la relación mínima como el factor de escala: min(ratioX, ratioY). De lo contrario, si el ancho es mayor, es posible que no se ajuste a la altura máxima.
Jason Sturges
ImageUtilities donde está?
tong
42

La mejor respuesta Maverick 1st se tradujo correctamente a Swift (trabajando con el último swift 3 ):

func imageWithImage (sourceImage:UIImage, scaledToWidth: CGFloat) -> UIImage {
    let oldWidth = sourceImage.size.width
    let scaleFactor = scaledToWidth / oldWidth

    let newHeight = sourceImage.size.height * scaleFactor
    let newWidth = oldWidth * scaleFactor

    UIGraphicsBeginImageContext(CGSize(width:newWidth, height:newHeight))
    sourceImage.draw(in: CGRect(x:0, y:0, width:newWidth, height:newHeight))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage!
}
Alessandro Ornano
fuente
38

gracias @ Maverick1st el algoritmo, lo implementé Swift, en mi caso la altura es el parámetro de entrada

class func resizeImage(image: UIImage, newHeight: CGFloat) -> UIImage {

    let scale = newHeight / image.size.height
    let newWidth = image.size.width * scale
    UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight))
    image.drawInRect(CGRectMake(0, 0, newWidth, newHeight))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage
}
János
fuente
18

Este método es una categoría en UIImage. Escala para ajustarse en pocas líneas de código usando AVFoundation. No olvides importar #import <AVFoundation/AVFoundation.h>.

@implementation UIImage (Helper)

- (UIImage *)imageScaledToFitToSize:(CGSize)size
{
    CGRect scaledRect = AVMakeRectWithAspectRatioInsideRect(self.size, CGRectMake(0, 0, size.width, size.height));
    UIGraphicsBeginImageContextWithOptions(size, NO, 0);
    [self drawInRect:scaledRect];
    UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return scaledImage;
}

@end
mohamede1945
fuente
2
Encontré esto para darme los mejores resultados con la menor huella de memoria. +1
Tommie C.
11

Versión Swift 5 de ajuste de aspecto a la altura , basada en la respuesta de @ János

Utiliza la UIGraphicsImageRendererAPI moderna , por lo que UIImagese garantiza una devolución válida .

extension UIImage
{
    /// Given a required height, returns a (rasterised) copy
    /// of the image, aspect-fitted to that height.

    func aspectFittedToHeight(_ newHeight: CGFloat) -> UIImage
    {
        let scale = newHeight / self.size.height
        let newWidth = self.size.width * scale
        let newSize = CGSize(width: newWidth, height: newHeight)
        let renderer = UIGraphicsImageRenderer(size: newSize)

        return renderer.image { _ in
            self.draw(in: CGRect(origin: .zero, size: newSize))
        }
    }
}

Puede usar esto junto con un recurso de imagen PDF (basado en vectores) para preservar la calidad en cualquier tamaño de renderizado.

Womble
fuente
7

La forma más simple es establecer el marco de su UIImageViewy establecer contentModeuna de las opciones de cambio de tamaño.

El código es así: esto se puede usar como un método de utilidad:

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize
{
    UIGraphicsBeginImageContext(newSize);
    [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
    UIGraphicsEndImageContext();
    return newImage;
}
Srikar Appalaraju
fuente
Tenga en cuenta que el OP no mencionó un UIImageView. En cambio, presumiblemente quería ajustar directamente una [copia de un] UIImage, lo cual es algo muy útil cuando estás trabajando en CoreGraphics.
Womble
6

Programáticamente:

imageView.contentMode = UIViewContentModeScaleAspectFit;

Storyboard:

ingrese la descripción de la imagen aquí

Mona
fuente
funciona, PERO reduciendo las huellas de memoria, puede hacer que la aplicación funcione más rápido, por ejemplo, cargar muchas miniaturas.
ingconti
Eso es un UIImageView, no un UIImage. A veces, debe dibujar sin un UIImageView.
Womble
5

Bueno, tenemos algunas buenas respuestas aquí para cambiar el tamaño de la imagen, pero solo modifico según mi necesidad. Espero que esto ayude a alguien como yo.

Mi requisito era

  1. Si el ancho de la imagen es superior a 1920, cambie el tamaño con un ancho de 1920 y mantenga la altura con la relación de aspecto original.
  2. Si la altura de la imagen es superior a 1080, cambie el tamaño con una altura de 1080 y mantenga el ancho con la relación de aspecto original.

 if (originalImage.size.width > 1920)
 {
        CGSize newSize;
        newSize.width = 1920;
        newSize.height = (1920 * originalImage.size.height) / originalImage.size.width;
        originalImage = [ProfileEditExperienceViewController imageWithImage:originalImage scaledToSize:newSize];        
 }

 if (originalImage.size.height > 1080)
 {
            CGSize newSize;
            newSize.width = (1080 * originalImage.size.width) / originalImage.size.height;
            newSize.height = 1080;
            originalImage = [ProfileEditExperienceViewController imageWithImage:originalImage scaledToSize:newSize];

 }

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize
{
        UIGraphicsBeginImageContext(newSize);
        [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
}

Gracias a @ Srikar Appal , por cambiar el tamaño, he usado su método.

Es posible que desee comprobar esto también para el cálculo del cambio de tamaño.

swiftBoy
fuente
4

Solo importe AVFoundation y use AVMakeRectWithAspectRatioInsideRect(CGRectCurrentSize, CGRectMake(0, 0, YOUR_WIDTH, CGFLOAT_MAX)

mariusLAN
fuente
2

Para mejorar la respuesta de Ryan:

   + (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)size {
CGFloat oldWidth = image.size.width;
        CGFloat oldHeight = image.size.height;

//You may need to take some retina adjustments into consideration here
        CGFloat scaleFactor = (oldWidth > oldHeight) ? width / oldWidth : height / oldHeight;

return [UIImage imageWithCGImage:image.CGImage scale:scaleFactor orientation:UIImageOrientationUp];
    }
TheCodingArt
fuente
2
extension UIImage {

    /// Returns a image that fills in newSize
    func resizedImage(newSize: CGSize) -> UIImage? {
        guard size != newSize else { return self }

    let hasAlpha = false
    let scale: CGFloat = 0.0
    UIGraphicsBeginImageContextWithOptions(newSize, !hasAlpha, scale)
        UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)

        draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
        let newImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage
    }

    /// Returns a resized image that fits in rectSize, keeping it's aspect ratio
    /// Note that the new image size is not rectSize, but within it.
    func resizedImageWithinRect(rectSize: CGSize) -> UIImage? {
        let widthFactor = size.width / rectSize.width
        let heightFactor = size.height / rectSize.height

        var resizeFactor = widthFactor
        if size.height > size.width {
            resizeFactor = heightFactor
        }

        let newSize = CGSize(width: size.width / resizeFactor, height: size.height / resizeFactor)
        let resized = resizedImage(newSize: newSize)

        return resized
    }
}

Cuando la escala se establece en 0.0, se usa el factor de escala de la pantalla principal, que para pantallas Retina es 2.0 o superior (3.0 en el iPhone 6 Plus).

oscarr
fuente
1

Ryan's Solution @Ryan en código rápido

utilizar:

 func imageWithSize(image: UIImage,size: CGSize)->UIImage{
    if UIScreen.mainScreen().respondsToSelector("scale"){
        UIGraphicsBeginImageContextWithOptions(size,false,UIScreen.mainScreen().scale);
    }
    else
    {
         UIGraphicsBeginImageContext(size);
    }

    image.drawInRect(CGRectMake(0, 0, size.width, size.height));
    var newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}

//Summon this function VVV
func resizeImageWithAspect(image: UIImage,scaledToMaxWidth width:CGFloat,maxHeight height :CGFloat)->UIImage
{
    let oldWidth = image.size.width;
    let oldHeight = image.size.height;

    let scaleFactor = (oldWidth > oldHeight) ? width / oldWidth : height / oldHeight;

    let newHeight = oldHeight * scaleFactor;
    let newWidth = oldWidth * scaleFactor;
    let newSize = CGSizeMake(newWidth, newHeight);

    return imageWithSize(image, size: newSize);
}
Daniel Krom
fuente
1

En Swift 3 hay algunos cambios. Aquí hay una extensión de UIImage:

public extension UIImage {
    public func resize(height: CGFloat) -> UIImage? {
        let scale = height / self.size.height
        let width = self.size.width * scale
        UIGraphicsBeginImageContext(CGSize(width: width, height: height))
        self.draw(in: CGRect(x:0, y:0, width:width, height:height))
        let resultImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return resultImage
    }
}
Eridana
fuente
1

Zeeshan Tufail y Womble responden en Swift 5 con pequeñas mejoras. Aquí tenemos una extensión con 2 funciones para escalar la imagen a maxLengthcualquier dimensión y compresión JPEG.

extension UIImage {
    func aspectFittedToMaxLengthData(maxLength: CGFloat, compressionQuality: CGFloat) -> Data {
        let scale = maxLength / max(self.size.height, self.size.width)
        let format = UIGraphicsImageRendererFormat()
        format.scale = scale
        let renderer = UIGraphicsImageRenderer(size: self.size, format: format)
        return renderer.jpegData(withCompressionQuality: compressionQuality) { context in
            self.draw(in: CGRect(origin: .zero, size: self.size))
        }
    }
    func aspectFittedToMaxLengthImage(maxLength: CGFloat, compressionQuality: CGFloat) -> UIImage? {
        let newImageData = aspectFittedToMaxLengthData(maxLength: maxLength, compressionQuality: compressionQuality)
        return UIImage(data: newImageData)
    }
}
Dmih
fuente
0

Este fue perfecto para mí. Mantiene la relación de aspecto y toma a maxLength. Ancho o Altura no será más demaxLength

-(UIImage*)imageWithImage: (UIImage*) sourceImage maxLength: (float) maxLength
{
    CGFloat scaleFactor = maxLength / MAX(sourceImage.size.width, sourceImage.size.height);

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

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

Calcula la mejor altura de la imagen para el ancho disponible.

import Foundation

public extension UIImage {
    public func height(forWidth width: CGFloat) -> CGFloat {
        let boundingRect = CGRect(
            x: 0,
            y: 0,
            width: width,
            height: CGFloat(MAXFLOAT)
        )
        let rect = AVMakeRect(
            aspectRatio: size,
            insideRect: boundingRect
        )
        return rect.size.height
    }
}
AshvinGudaliya
fuente
-1

Lo he resuelto de una manera mucho más simple.

UIImage *placeholder = [UIImage imageNamed:@"OriginalImage.png"];
self.yourImageview.image = [UIImage imageWithCGImage:[placeholder CGImage] scale:(placeholder.scale * 1.5)
                                  orientation:(placeholder.imageOrientation)];

El multiplicador de la escala definirá la escala de la imagen, cuanto más pequeño sea el multiplicador. para que pueda verificar qué se ajusta a su pantalla.

O también puede obtener el multiplicador dividiendo el ancho de imagen / ancho de pantalla.

Siddhant Saxena
fuente