¿La forma más sencilla de cambiar el tamaño de un UIImage?

397

En la aplicación de mi iPhone, tomo una foto con la cámara, luego quiero cambiar su tamaño a 290 * 390 píxeles. Estaba usando este método para cambiar el tamaño de la imagen:

UIImage *newImage = [image _imageScaledToSize:CGSizeMake(290, 390)
                         interpolationQuality:1];    

Funciona perfectamente, pero es una función indocumentada, por lo que ya no puedo usarla con iPhone OS4.

Entonces ... ¿cuál es la forma más sencilla de cambiar el tamaño de un UIImage?

pimpampoum
fuente
La forma de hacerlo en 2019, nshipster.com/image-resizing
dengST30

Respuestas:

772

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

O puede usar este método de utilidad, si realmente necesita cambiar el tamaño de una imagen:

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
    //UIGraphicsBeginImageContext(newSize);
    // In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
    // Pass 1.0 to force exact pixel size.
    UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
    [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
    UIGraphicsEndImageContext();
    return newImage;
}

Ejemplo de uso:

#import "MYUtil.h"

UIImage *myIcon = [MYUtil imageWithImage:myUIImageInstance scaledToSize:CGSizeMake(20, 20)];
Paul Lynch
fuente
11
NO retendría el resultado aquí. newImageya se lanzará automáticamente, y debería depender del método que lo llamó para retenerlo o no. Este enfoque solo está pidiendo un error de memoria ya initque no es parte del nombre del método. Esperaría que un método llamado así devuelva un objeto lanzado automáticamente.
Alex Wayne
Punto válido y editado. Solo dejaré la advertencia de que mi código que usa esto genera errores de malloc en otro lugar sin una retención adicional, lo cual es claramente mi culpa de alguna manera :-).
Paul Lynch
26
A partir de iOS 4.0 , las funciones de UIGraphics * son todas seguras para subprocesos.
BJ Homer
3
@Paul Lynch: Tenga en cuenta que este cambio rompe la misión de la publicación original: cómo cambiar el tamaño de una imagen a un tamaño de píxel exacto (en oposición al tamaño de punto ).
Nikolai Ruhe
8
Para aquellos que luchan debido a la limitación que señaló @NikolaiRuhe, puede forzarlo a un tamaño de píxel exacto pasando 1.0 en lugar de 0.0 en la llamada aUIGraphicsBeginImageContextWithOptions
Danny
84

Aquí hay una versión rápida de la respuesta de Paul Lynch

func imageWithImage(image:UIImage, scaledToSize newSize:CGSize) -> UIImage{
    UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0);
    image.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height))
    let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage
}

Y como extensión:

public extension UIImage {
    func copy(newSize: CGSize, retina: Bool = true) -> UIImage? {
        // In next line, pass 0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
        // Pass 1 to force exact pixel size.
        UIGraphicsBeginImageContextWithOptions(
            /* size: */ newSize,
            /* opaque: */ false,
            /* scale: */ retina ? 0 : 1
        )
        defer { UIGraphicsEndImageContext() }

        self.draw(in: CGRect(origin: .zero, size: newSize))
        return UIGraphicsGetImageFromCurrentImageContext()
    }
}
Rogerio Chaves
fuente
2
solo noté que, en iOS, su nuevo tamaño se multiplicará por 2x, 3x según el dispositivo
Sruit A.Suk
66
UIGraphicsBeginImageContextWithOptions (newSize, false, image.scale); para todos los dispositivos
phnmnn
Tenga en cuenta que esto no cambia el tamaño de la imagen CG subyacente (al menos en iOS 12.1). Lo cual está bien según la pregunta, pero si está escribiendo la imagen, tendrá que usar cgImage y obtendrá la imagen original.
prewett
72

Solución Proper Swift 3.0 para iOS 10+ : uso ImageRenderery sintaxis de cierre:

func imageWith(newSize: CGSize) -> UIImage {
    let renderer = UIGraphicsImageRenderer(size: newSize)
    let image = renderer.image { _ in
        self.draw(in: CGRect.init(origin: CGPoint.zero, size: newSize))
    }

    return image.withRenderingMode(self.renderingMode)
}

Y aquí está la versión de Objective-C:

@implementation UIImage (ResizeCategory)
- (UIImage *)imageWithSize:(CGSize)newSize
{
    UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:newSize];
    UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext*_Nonnull myContext) {
        [self drawInRect:(CGRect) {.origin = CGPointZero, .size = newSize}];
    }];
    return [image imageWithRenderingMode:self.renderingMode];
}
@end
Jakub Průša
fuente
1
¡Una solución muy elegante, esta debería ser la respuesta aceptada!
appsunited
2
Esto es mejor que elegante, es correcto. Los documentos api de Apple recomiendan un nuevo código para usar UIGraphicsRenderer como lo hace este código. Gracias
Paul Bruneau
1
@appsunited La pregunta tiene casi 8 años. Y este método solo funciona para iOS10 + como se indicó.
Al copiar el nuevo objeto de imagen en el portapapeles, produce un bloque blanco para mí. ¿No está cambiando realmente el tamaño de la vista de imagen?
Oliver Dixon
1
Esta función devuelve una imagen de 0 de altura y 0 de ancho, se espera que la altura sea de 9000 de altura y 9000 de ancho.
krishan kumar
31

Trevor Howard tiene algunas categorías UIImage que manejan el cambio de tamaño bastante bien. Si nada más puede usar el código como ejemplos.

Nota: A partir de iOS 5.1, esta respuesta puede ser inválida. Ver comentario a continuación.

TechZen
fuente
2
También necesitaba cambiar el tamaño de algunas imágenes, y primero probé las adiciones de Trevor a UIImage, pero obtuve algunos errores extraños en PNG (algo sobre el canal alfa). Sin embargo, la respuesta aceptada a esta pregunta funcionó muy bien.
Sorig
1
Esto ya no es válido (al menos con iOS5.1
Jann
@ Janan, mira las confirmaciones debajo de la publicación.
vikingosegundo
@ Jan Si sigues las soluciones sugeridas en los comentarios, ve con "Matt dice: 22 de noviembre de 2011 a las 5:39 pm" porque libera el colorpaceref (a diferencia de la solución similar de horseshoe7).
Ben Flynn
1
@sanmai utilizando cualquiera de estos métodos de cambio de tamaño, las imágenes pierden su nitidez. ¿hay alguna manera de lograr la nitidez
vamsi575kg
31

Una versión más compacta para Swift 4 y iOS 10+:

extension UIImage {
    func resized(to size: CGSize) -> UIImage {
        return UIGraphicsImageRenderer(size: size).image { _ in
            draw(in: CGRect(origin: .zero, size: size))
        }
    }
}

Uso:

let resizedImage = image.resized(to: CGSize(width: 50, height: 50))
neave
fuente
26

También he visto esto también (que uso en el UIButtonsestado Normal y Seleccionado ya que los botones no resizeencajan). El crédito va para quien sea el autor original.

Primero haga un archivo .h y .m vacío llamado UIImageResizing.hyUIImageResizing.m

// Put this in UIImageResizing.h
@interface UIImage (Resize)
- (UIImage*)scaleToSize:(CGSize)size;
@end

// Put this in UIImageResizing.m
@implementation UIImage (Resize)

- (UIImage*)scaleToSize:(CGSize)size {
UIGraphicsBeginImageContext(size);

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0.0, size.height);
CGContextScaleCTM(context, 1.0, -1.0);

CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, size.width, size.height), self.CGImage);

UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return scaledImage;
}

@end

Incluya ese archivo .h en cualquier archivo .m en el que vaya a utilizar la función y luego llámelo de esta manera:

UIImage* image = [UIImage imageNamed:@"largeImage.png"];
UIImage* smallImage = [image scaleToSize:CGSizeMake(100.0f,100.0f)];
Me han robado
fuente
Esto no copia sobre la orientación de la imagen.
Kevin
20

Esta mejora en el código de Paul le dará una imagen nítida de alta resolución en un iPhone con pantalla retina. De lo contrario, cuando se reduce, es borroso.

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
    if ([[UIScreen mainScreen] scale] == 2.0) {
        UIGraphicsBeginImageContextWithOptions(newSize, YES, 2.0);
    } else {
        UIGraphicsBeginImageContext(newSize);
    }
} else {
    UIGraphicsBeginImageContext(newSize);
}
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
UIGraphicsEndImageContext();
return newImage;
}
malhal
fuente
13
Solo para su información, los cambios que ha realizado son innecesarios, ya que el suministro de un valor de 0.0 para la escala UIGraphicsBeginImageContextWithOptionsutilizará automáticamente la escala de la pantalla principal (a partir de iOS 3.2).
Andrew R.
16

Aquí hay una manera simple:

    UIImage * image = [UIImage imageNamed:@"image"];
    CGSize sacleSize = CGSizeMake(10, 10);
    UIGraphicsBeginImageContextWithOptions(sacleSize, NO, 0.0);
    [image drawInRect:CGRectMake(0, 0, sacleSize.width, sacleSize.height)];
    UIImage * resizedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

resizedImage es una nueva imagen.

asmad
fuente
16

Solución rápida para Stretch Fill, Aspect Fill y Aspect Fit

extension UIImage {
    enum ContentMode {
        case contentFill
        case contentAspectFill
        case contentAspectFit
    }

    func resize(withSize size: CGSize, contentMode: ContentMode = .contentAspectFill) -> UIImage? {
        let aspectWidth = size.width / self.size.width
        let aspectHeight = size.height / self.size.height

        switch contentMode {
        case .contentFill:
            return resize(withSize: size)
        case .contentAspectFit:
            let aspectRatio = min(aspectWidth, aspectHeight)
            return resize(withSize: CGSize(width: self.size.width * aspectRatio, height: self.size.height * aspectRatio))
        case .contentAspectFill:
            let aspectRatio = max(aspectWidth, aspectHeight)
            return resize(withSize: CGSize(width: self.size.width * aspectRatio, height: self.size.height * aspectRatio))
        }
    }

    private func resize(withSize size: CGSize) -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(size, false, self.scale)
        defer { UIGraphicsEndImageContext() }
        draw(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
        return UIGraphicsGetImageFromCurrentImageContext()
    }
}

y para usar puedes hacer lo siguiente:

let image = UIImage(named: "image.png")!
let newImage = image.resize(withSize: CGSize(width: 200, height: 150), contentMode: .contentAspectFill)

Gracias a abdullahselek por su solución original.

Josh Bernfeld
fuente
15

Aquí hay una modificación de la categoría escrita por iWasRobbed arriba. Mantiene la relación de aspecto de la imagen original en lugar de distorsionarla.

- (UIImage*)scaleToSizeKeepAspect:(CGSize)size {
    UIGraphicsBeginImageContext(size);

    CGFloat ws = size.width/self.size.width;
    CGFloat hs = size.height/self.size.height;

    if (ws > hs) {
        ws = hs/ws;
        hs = 1.0;
    } else {
        hs = ws/hs;
        ws = 1.0;
    }

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(context, 0.0, size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    CGContextDrawImage(context, CGRectMake(size.width/2-(size.width*ws)/2,
        size.height/2-(size.height*hs)/2, size.width*ws,
        size.height*hs), self.CGImage);

    UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return scaledImage;
}
Scott significa
fuente
11

Si solo desea una imagen más pequeña y no le importa el tamaño exacto:

+ (UIImage *)imageWithImage:(UIImage *)image scaledToScale:(CGFloat)scale
{
    UIGraphicsBeginImageContextWithOptions(self.size, YES, scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
    [self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

Si configura la escala 0.25f, obtendrá una imagen de 816 por 612 de una cámara de 8MP.

Aquí hay una categoría UIImage + Scale para aquellos que la necesitan.

sanmai
fuente
10

¿Por qué tan complicado? Creo que usar la API del sistema puede lograr el mismo resultado:

UIImage *largeImage;
CGFloat ratio = 0.4; // you want to get a new image that is 40% the size of large image.
UIImage *newImage = [UIImage imageWithCGImage:largeImage.CGImage
                                        scale:1/ratio
                                  orientation:largeImage.imageOrientation];
// notice the second argument, it is 1/ratio, not ratio.

El único problema es que debe pasar el inverso del índice objetivo como segundo argumento, ya que según el documento, el segundo parámetro especifica el índice de la imagen original en comparación con el nuevo escalado.

Chris
fuente
Porque la escala (%) no es lo mismo que los píxeles (w / h).
La escala debe ser largeImage.scale / ratio, no 1 / ratio.
Marián Černý
9

Esta es una extensión UIImage compatible con Swift 3 y Swift 4 que escala la imagen al tamaño dado con una relación de aspecto

extension UIImage {

    func scaledImage(withSize size: CGSize) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
        defer { UIGraphicsEndImageContext() }
        draw(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
        return UIGraphicsGetImageFromCurrentImageContext()!
    }

    func scaleImageToFitSize(size: CGSize) -> UIImage {
        let aspect = self.size.width / self.size.height
        if size.width / aspect <= size.height {
            return scaledImage(withSize: CGSize(width: size.width, height: size.width / aspect))
        } else {
            return scaledImage(withSize: CGSize(width: size.height * aspect, height: size.height))
        }
    }

}

Ejemplo de uso

let image = UIImage(named: "apple")
let scaledImage = image.scaleImageToFitSize(size: CGSize(width: 45.0, height: 45.0))
abdullahselek
fuente
Yo recomendaría nombrar la función scaledImage(with size:)o scaledWithSize(_:). Tampoco UIGraphicsGetImageFromCurrentImageContextpuede volver realmente nil, por !lo tanto , funcionaría también. También puede simplificar la creación de rect a CGRect(origin: .zero, size: size).
Sulthan
6
 func resizeImage(image: UIImage, newWidth: CGFloat) -> UIImage 
{
        let scale = newWidth / image.size.width
        let newHeight = image.size.height * scale
        UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight))
        image.drawInRect(CGRectMake(0, 0, newWidth, newHeight))
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage
}
ashwini
fuente
Es una buena idea agregar un texto descriptivo que vaya con su código.
GMchris
6

Para mis compañeros Xamarianos, aquí hay una versión Xamarin.iOS C # de la respuesta de @Paul Lynch.

private UIImage ResizeImage(UIImage image, CGSize newSize) 
{
    UIGraphics.BeginImageContextWithOptions(newSize, false, 0.0f);
    image.Draw(new CGRect(0, 0, newSize.Width, newSize.Height));
    UIImage newImage = UIGraphics.GetImageFromCurrentImageContext();
    UIGraphics.EndImageContext();
    return newImage;
}
Martin Zikmund
fuente
5

Si desea hacer una miniatura de un UIImage (con cambio de tamaño proporcional o tal vez algún recorte involucrado), consulte la categoría UIImage + Resize que le permite usar una sintaxis concisa, similar a ImageMagick:

UIImage* squareImage       = [image resizedImageByMagick: @"320x320#"];
Vlad Andersen
fuente
5

[cf Chris] Para cambiar el tamaño al tamaño deseado:

UIImage *after = [UIImage imageWithCGImage:before.CGImage
                                     scale:CGImageGetHeight(before.CGImage)/DESIREDHEIGHT
                               orientation:UIImageOrientationUp];

o, equivalentemente, sustituto CGImageGetWidth(...)/DESIREDWIDTH

tiritea
fuente
Tenga en cuenta que los datos de la imagen en sí no cambian de tamaño, pero se aplica un factor de escala que significa que la imagen tiene el mismo tamaño en la memoria.
inkredibl
5

Rogerio Chaves responde como una extensión rápida

func scaledTo(size: CGSize) -> UIImage{
    UIGraphicsBeginImageContextWithOptions(size, false, 0.0);
    self.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
    let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()
    return newImage
}

Y también bonificación

func scaledTo(height: CGFloat) -> UIImage{
    let width = height*self.size.width/self.size.height
    return scaledTo(size: CGSize(width: width, height: height))
}
Intenciones
fuente
5

Swift 3.0 con opción a prueba de fallos (devuelve la imagen original en caso de error):

func resize(image: UIImage, toSize size: CGSize) -> UIImage{
    UIGraphicsBeginImageContextWithOptions(size,false,1.0)
    image.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
    if let resizedImage = UIGraphicsGetImageFromCurrentImageContext() {
        UIGraphicsEndImageContext()
        return resizedImage
    }
    UIGraphicsEndImageContext()
    return image
}
rowel
fuente
5

(Compatible con Swift 4) Solución iOS 10+ e iOS <10 (utilizando, UIGraphicsImageRenderersi es posible, de lo UIGraphicsGetImageFromCurrentImageContextcontrario)

/// Resizes an image
///
/// - Parameter newSize: New size
/// - Returns: Resized image
func scaled(to newSize: CGSize) -> UIImage {
    let rect = CGRect(origin: .zero, size: newSize)

    if #available(iOS 10, *) {
        let renderer = UIGraphicsImageRenderer(size: newSize)
        return renderer.image { _ in
            self.draw(in: rect)
        }
    } else {
        UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
        self.draw(in: rect)
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage!
    }
}
Nathan
fuente
5

Enfoque efectivo sin estirar la imagen Swift 4

// Method to resize image
func resize(image: UIImage, toScaleSize:CGSize) -> UIImage {
                UIGraphicsBeginImageContextWithOptions(toScaleSize, true, image.scale)
                        image.draw(in: CGRect(x: 0, y: 0, width: toScaleSize.width, height: toScaleSize.height))
                        let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
                        UIGraphicsEndImageContext()
                        return scaledImage!
                }

// Método de llamada

    let resizedImage = self.resize(image: UIImage(named: "YourImageName")!, toScaleSize: CGSize(width: 290, height: 390))
equipo iOS
fuente
3

La respuesta de @Paul Lynch es excelente, pero cambiaría la relación de la imagen. si no desea cambiar la proporción de la imagen y aún desea que la nueva imagen se ajuste al nuevo tamaño, intente esto.

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {

// calculate a new size which ratio is same to original image
CGFloat ratioW = image.size.width / newSize.width;
CGFloat ratioH = image.size.height / newSize.height;

CGFloat ratio = image.size.width / image.size.height;

CGSize showSize = CGSizeZero;
if (ratioW > 1 && ratioH > 1) { 

    if (ratioW > ratioH) { 
        showSize.width = newSize.width;
        showSize.height = showSize.width / ratio;
    } else {
        showSize.height = newSize.height;
        showSize.width = showSize.height * ratio;
    }

} else if (ratioW > 1) {

    showSize.width = showSize.width;
    showSize.height = showSize.width / ratio;

} else if (ratioH > 1) {

    showSize.height = showSize.height;
    showSize.width = showSize.height * ratio;

}

//UIGraphicsBeginImageContext(newSize);
// In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
// Pass 1.0 to force exact pixel size.
UIGraphicsBeginImageContextWithOptions(showSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, showSize.width, showSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;}
wossoneri
fuente
3

usa esta extensión

extension UIImage {
    public func resize(size:CGSize, completionHandler:(resizedImage:UIImage, data:NSData?)->()) {
        dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), { () -> Void in
            let newSize:CGSize = size
            let rect = CGRectMake(0, 0, newSize.width, newSize.height)
            UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
            self.drawInRect(rect)
            let newImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            let imageData = UIImageJPEGRepresentation(newImage, 0.5)
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                completionHandler(resizedImage: newImage, data:imageData)
            })
        })
    }
}
Beslan Tularov
fuente
2

Swift 2.0:

let image = UIImage(named: "imageName")
let newSize = CGSize(width: 10, height: 10)

UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
image?.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height))
let imageResized = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Phil
fuente
2

Aquí mi código Swift algo detallado

func scaleImage(image:UIImage,  toSize:CGSize) -> UIImage {
    UIGraphicsBeginImageContextWithOptions(toSize, false, 0.0);

    let aspectRatioAwareSize = self.aspectRatioAwareSize(image.size, boxSize: toSize, useLetterBox: false)


    let leftMargin = (toSize.width - aspectRatioAwareSize.width) * 0.5
    let topMargin = (toSize.height - aspectRatioAwareSize.height) * 0.5


    image.drawInRect(CGRectMake(leftMargin, topMargin, aspectRatioAwareSize.width , aspectRatioAwareSize.height))
    let retVal = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return retVal
}

func aspectRatioAwareSize(imageSize: CGSize, boxSize: CGSize, useLetterBox: Bool) -> CGSize {
    // aspect ratio aware size
    // http://stackoverflow.com/a/6565988/8047
    let imageWidth = imageSize.width
    let imageHeight = imageSize.height
    let containerWidth = boxSize.width
    let containerHeight = boxSize.height

    let imageAspectRatio = imageWidth/imageHeight
    let containerAspectRatio = containerWidth/containerHeight

    let retVal : CGSize
    // use the else at your own risk: it seems to work, but I don't know 
    // the math
    if (useLetterBox) {
        retVal = containerAspectRatio > imageAspectRatio ? CGSizeMake(imageWidth * containerHeight / imageHeight, containerHeight) : CGSizeMake(containerWidth, imageHeight * containerWidth / imageWidth)
    } else {
        retVal = containerAspectRatio < imageAspectRatio ? CGSizeMake(imageWidth * containerHeight / imageHeight, containerHeight) : CGSizeMake(containerWidth, imageHeight * containerWidth / imageWidth)
    }

    return retVal
}
Dan Rosenstark
fuente
1
Esto me ahorró tiempo. ¡Gracias!
Quy Nguyen
útil, pero actúa de manera extraña después de seguir y volver a las actividades, mira aquí por favor: stackoverflow.com/questions/30978685/…
He Yifei 何 一 非
Tenga en cuenta: es posible que desee agregar una condición previa ( guard) a la aspectRatioAwareSizefunción de modo que no arroje una excepción de división por cero si los parámetros de entrada para imageSize o boxSize sonCGSize.zero
pkluz
@pkluz esta es una buena sugerencia, pero creo que los necesitaría en toda la función, ya que cada variable es el divisor de otra línea ... Voy a dejar esto como es para mayor claridad. Para la producción o una biblioteca para github, es posible que desee reforzar esto ... Más que un guardia, me preocupa que la función use nombres de variables cortos que no dicen mucho. Probablemente lo arreglaría primero, y luego agregaría guardias para otras cosas, y luego agregaría pruebas unitarias.
Dan Rosenstark
2

Respuesta rápida 4:

func scaleDown(image: UIImage, withSize: CGSize) -> UIImage {
    let scale = UIScreen.main.scale
    UIGraphicsBeginImageContextWithOptions(withSize, false, scale)
    image.draw(in: CGRect(x: 0, y: 0, width: withSize.width, height: withSize.height))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage!
}
DanielEdrisian
fuente
No es necesario establecer la escala real:The scale factor to apply to the bitmap. If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen.
nathan
1

Descubrí que es difícil encontrar una respuesta que pueda usar de forma inmediata en su proyecto Swift 3 . El principal problema de otras respuestas es que no respetan el canal alfa de la imagen. Aquí está la técnica que estoy usando en mis proyectos.

extension UIImage {

    func scaledToFit(toSize newSize: CGSize) -> UIImage {
        if (size.width < newSize.width && size.height < newSize.height) {
            return copy() as! UIImage
        }

        let widthScale = newSize.width / size.width
        let heightScale = newSize.height / size.height

        let scaleFactor = widthScale < heightScale ? widthScale : heightScale
        let scaledSize = CGSize(width: size.width * scaleFactor, height: size.height * scaleFactor)

        return self.scaled(toSize: scaledSize, in: CGRect(x: 0.0, y: 0.0, width: scaledSize.width, height: scaledSize.height))
    }

    func scaled(toSize newSize: CGSize, in rect: CGRect) -> UIImage {
        if UIScreen.main.scale == 2.0 {
            UIGraphicsBeginImageContextWithOptions(newSize, !hasAlphaChannel, 2.0)
        }
        else {
            UIGraphicsBeginImageContext(newSize)
        }

        draw(in: rect)
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage ?? UIImage()
    }

    var hasAlphaChannel: Bool {
        guard let alpha = cgImage?.alphaInfo else {
            return false
        }
        return alpha == CGImageAlphaInfo.first ||
            alpha == CGImageAlphaInfo.last ||
            alpha == CGImageAlphaInfo.premultipliedFirst ||
            alpha == CGImageAlphaInfo.premultipliedLast
    }
}

Ejemplo de uso:

override func viewDidLoad() {
    super.viewDidLoad()

    let size = CGSize(width: 14.0, height: 14.0)
    if let image = UIImage(named: "barbell")?.scaledToFit(toSize: size) {
        let imageView = UIImageView(image: image)
        imageView.center = CGPoint(x: 100, y: 100)
        view.addSubview(imageView)
    }
}

Este código es una reescritura de la extensión de Apple con soporte adicional para imágenes con y sin canal alfa.

Como lectura adicional, recomiendo consultar este artículo para conocer diferentes técnicas de cambio de tamaño de imagen. El enfoque actual ofrece un rendimiento decente, opera API de alto nivel y es fácil de entender. Recomiendo mantenerlo a menos que encuentre que el cambio de tamaño de la imagen es un cuello de botella en su rendimiento.

Vadim Bulavin
fuente
1

Use esta extensión, en caso de que necesite cambiar el tamaño del ancho / alto solo con la relación de aspecto.

extension UIImage {
    func resize(to size: CGSize) -> UIImage {
        return UIGraphicsImageRenderer(size: size).image { _ in
            draw(in: CGRect(origin: .zero, size: size))
        }
    }
    func resize(width: CGFloat) -> UIImage {
        return resize(to: CGSize(width: width, height: width / (size.width / size.height)))
    }
    func resize(height: CGFloat) -> UIImage {
        return resize(to: CGSize(width: height * (size.width / size.height), height: height))
    }
}
Jeffrey Neo
fuente