¿Cuál es la forma más sencilla de cambiar el tamaño / optimizar un tamaño de image con el iPhone SDK?

Mi aplicación descarga un set de files de imágenes de la networking y los guarda en el disco local de iPhone. Algunas de esas imágenes son bastante grandes (ancho mayor que 500 píxeles, por ejemplo). Dado que el iPhone ni siquiera tiene una pantalla lo suficientemente grande como para mostrar la image en su tamaño original, estoy planeando cambiar el tamaño de la image a algo un poco más pequeño para ahorrar espacio / performance.

Además, algunas de esas imágenes son JPEG y no se guardan como la configuration habitual de 60% de calidad.

¿Cómo puedo cambiar el tamaño de una image con el iPhone SDK y cómo puedo cambiar la configuration de calidad de una image JPEG?

Se proporcionan algunas sugerencias para responder a esta pregunta . Sugerí la técnica descrita en esta publicación , con el código relevante:

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

En cuanto al almacenamiento de la image, el formatting de image más rápido para usar con el iPhone es PNG, ya que tiene optimizaciones para ese formatting. Sin embargo, si desea almacenar estas imágenes como files JPEG, puede tomar su UIImage y hacer lo siguiente:

 NSData *dataForJPEGFile = UIImageJPEGRepresentation(theImage, 0.6); 

Esto crea una instancia de NSData que contiene los bytes sin formatting para una image JPEG en una configuration de 60% de calidad. El contenido de esa instancia de NSData se puede grabar en el disco o almacenarse en memory caching.

La forma más fácil y directa de cambiar el tamaño de tus imágenes sería este

 float actualHeight = image.size.height; float actualWidth = image.size.width; float imgRatio = actualWidth/actualHeight; float maxRatio = 320.0/480.0; if(imgRatio!=maxRatio){ if(imgRatio < maxRatio){ imgRatio = 480.0 / actualHeight; actualWidth = imgRatio * actualWidth; actualHeight = 480.0; } else{ imgRatio = 320.0 / actualWidth; actualHeight = imgRatio * actualHeight; actualWidth = 320.0; } } CGRect rect = CGRectMake(0.0, 0.0, actualWidth, actualHeight); UIGraphicsBeginImageContext(rect.size); [image drawInRect:rect]; UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); 

Los methods anteriores funcionan bien para imágenes pequeñas, pero cuando intenta cambiar el tamaño de una image muy grande, se quedará rápidamente sin memory y bloqueará la aplicación. Una manera mucho mejor es usar CGImageSourceCreateThumbnailAtIndex para cambiar el tamaño de la image sin decodificarlo por completo primero.

Si tiene la ruta a la image que quiere cambiar de tamaño, puede usar esto:

 - (void)resizeImageAtPath:(NSString *)imagePath { // Create the image source (from path) CGImageSourceRef src = CGImageSourceCreateWithURL((__bridge CFURLRef) [NSURL fileURLWithPath:imagePath], NULL); // To create image source from UIImage, use this // NSData* pngData = UIImagePNGRepresentation(image); // CGImageSourceRef src = CGImageSourceCreateWithData((CFDataRef)pngData, NULL); // Create thumbnail options CFDictionaryRef options = (__bridge CFDictionaryRef) @{ (id) kCGImageSourceCreateThumbnailWithTransform : @YES, (id) kCGImageSourceCreateThumbnailFromImageAlways : @YES, (id) kCGImageSourceThumbnailMaxPixelSize : @(640) }; // Generate the thumbnail CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(src, 0, options); CFRelease(src); // Write the thumbnail at path CGImageWriteToFile(thumbnail, imagePath); } 

Más detalles aquí .

La mejor forma de escalar imágenes sin perder la relación de aspecto (es decir, sin estirar el dispositivo) es usar este método:

 //to scale images without changing aspect ratio + (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)newSize { float width = newSize.width; float height = newSize.height; UIGraphicsBeginImageContext(newSize); CGRect rect = CGRectMake(0, 0, width, height); float widthRatio = image.size.width / width; float heightRatio = image.size.height / height; float divisor = widthRatio > heightRatio ? widthRatio : heightRatio; width = image.size.width / divisor; height = image.size.height / divisor; rect.size.width = width; rect.size.height = height; //indent in case of width or height difference float offset = (width - height) / 2; if (offset > 0) { rect.origin.y = offset; } else { rect.origin.x = -offset; } [image drawInRect: rect]; UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return smallImage; } 

Agregue este método a su class de utilidad para que pueda usarlo en todo su proyecto y acceda así:

 xyzImageView.image = [Utility scaleImage:yourUIImage toSize:xyzImageView.frame.size]; 

Este método se encarga de la escala mientras se mantiene la relación de aspecto. También agrega sangrías a la image en caso de que la image networkingucida tenga más ancho que alto (o viceversa).

Si tiene control sobre el server, recomiendo cambiar el tamaño del lado del server de imágenes con ImageMagik . Descargar imágenes grandes y cambiar su tamaño en el teléfono es una pérdida de muchos resources preciosos: ancho de banda, batería y memory. Todos los cuales son escasos en los teléfonos.

Desarrollé una solución definitiva para escalar imágenes en Swift.

Puede usarlo para cambiar el tamaño de la image para que se llene, el aspecto se rellene o el aspecto se ajuste al tamaño especificado.

Puede alinear la image al centro o cualquiera de los cuatro bordes y cuatro esquinas.

Y también puede recortar el espacio adicional que se agrega si las proporciones de aspecto de la image original y el tamaño de destino no son iguales.

 enum UIImageAlignment { case Center, Left, Top, Right, Bottom, TopLeft, BottomRight, BottomLeft, TopRight } enum UIImageScaleMode { case Fill, AspectFill, AspectFit(UIImageAlignment) } extension UIImage { func scaleImage(width width: CGFloat? = nil, height: CGFloat? = nil, scaleMode: UIImageScaleMode = .AspectFit(.Center), trim: Bool = false) -> UIImage { let preWidthScale = width.map { $0 / size.width } let preHeightScale = height.map { $0 / size.height } var widthScale = preWidthScale ?? preHeightScale ?? 1 var heightScale = preHeightScale ?? widthScale switch scaleMode { case .AspectFit(_): let scale = min(widthScale, heightScale) widthScale = scale heightScale = scale case .AspectFill: let scale = max(widthScale, heightScale) widthScale = scale heightScale = scale default: break } let newWidth = size.width * widthScale let newHeight = size.height * heightScale let canvasWidth = trim ? newWidth : (width ?? newWidth) let canvasHeight = trim ? newHeight : (height ?? newHeight) UIGraphicsBeginImageContextWithOptions(CGSizeMake(canvasWidth, canvasHeight), false, 0) var originX: CGFloat = 0 var originY: CGFloat = 0 switch scaleMode { case .AspectFit(let alignment): switch alignment { case .Center: originX = (canvasWidth - newWidth) / 2 originY = (canvasHeight - newHeight) / 2 case .Top: originX = (canvasWidth - newWidth) / 2 case .Left: originY = (canvasHeight - newHeight) / 2 case .Bottom: originX = (canvasWidth - newWidth) / 2 originY = canvasHeight - newHeight case .Right: originX = canvasWidth - newWidth originY = (canvasHeight - newHeight) / 2 case .TopLeft: break case .TopRight: originX = canvasWidth - newWidth case .BottomLeft: originY = canvasHeight - newHeight case .BottomRight: originX = canvasWidth - newWidth originY = canvasHeight - newHeight } default: break } self.drawInRect(CGRectMake(originX, originY, newWidth, newHeight)) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image } } 

Hay ejemplos de la aplicación de esta solución a continuación.

El rectángulo gris es la image del sitio objective que se cambiará de tamaño. Los círculos azules en rectángulo azul claro son la image (usé círculos porque es fácil de ver cuando está escalado sin preservar aspecto). El color naranja claro marca las áreas que se recortarán si pasas trim: true .

Ajuste del aspecto antes y después de la escala:

Ajuste del aspecto 1 (antes) Ajuste del aspecto 1 (después)

Otro ejemplo de ajuste de aspecto :

Ajuste del aspecto 2 (antes) Ajuste del aspecto 2 (después)

Ajuste del aspecto con la alignment superior:

Ajuste del aspecto 3 (antes) Ajuste del aspecto 3 (después)

Relleno de aspecto :

Aspecto de relleno (antes) Aspecto de relleno (después)

Rellenar :

Rellenar (antes) Rellenar (después)

Utilicé la mejora de escala en mis ejemplos porque es más simple de demostrar, pero la solución también funciona para networkingucir la escala como en cuestión.

Para la compression JPEG, debe usar esto:

 let compressionQuality: CGFloat = 0.75 // adjust to change JPEG quality if let data = UIImageJPEGRepresentation(image, compressionQuality) { // ... } 

Puedes ver mi esencia con el parque infantil Xcode.

Puede usar este código para escalar la image en el tamaño requerido.

 + (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)newSize { CGSize actSize = image.size; float scale = actSize.width/actSize.height; if (scale < 1) { newSize.height = newSize.width/scale; } else { newSize.width = newSize.height*scale; } UIGraphicsBeginImageContext(newSize); [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)]; UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newImage; } 

Para Swift 3, el código de abajo escala la image manteniendo la relación de aspecto. Puede leer más sobre el ImageContext en la documentation de Apple :

 extension UIImage { class func resizeImage(image: UIImage, newHeight: CGFloat) -> UIImage { let scale = newHeight / image.size.height let newWidth = image.size.width * scale UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight)) image.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight)) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! } } 

Para usarlo, llame resizeImage() método resizeImage() :

 UIImage.resizeImage(image: yourImageName, newHeight: yourImageNewHeight) 

Si alguien todavía está buscando una mejor opción

 -(UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)targetSize { UIImage *sourceImage = image; UIImage *newImage = nil; CGSize imageSize = sourceImage.size; CGFloat width = imageSize.width; CGFloat height = imageSize.height; CGFloat targetWidth = targetSize.width; CGFloat targetHeight = targetSize.height; CGFloat scaleFactor = 0.0; CGFloat scaledWidth = targetWidth; CGFloat scaledHeight = targetHeight; CGPoint thumbnailPoint = CGPointMake(0.0,0.0); if (CGSizeEqualToSize(imageSize, targetSize) == NO) { CGFloat widthFactor = targetWidth / width; CGFloat heightFactor = targetHeight / height; if (widthFactor < heightFactor) scaleFactor = widthFactor; else scaleFactor = heightFactor; scaledWidth = width * scaleFactor; scaledHeight = height * scaleFactor; // center the image if (widthFactor < heightFactor) { thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5; } else if (widthFactor > heightFactor) { thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5; } } // this is actually the interesting part: UIGraphicsBeginImageContext(targetSize); CGRect thumbnailRect = CGRectZero; thumbnailRect.origin = thumbnailPoint; thumbnailRect.size.width = scaledWidth; thumbnailRect.size.height = scaledHeight; [sourceImage drawInRect:thumbnailRect]; newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); if(newImage == nil) NSLog(@"could not scale image"); return newImage ; } 

Un problema que puede ocurrir en las pantallas de retina es que ImageCapture establece la escala de la image más o less. Las funciones de cambio de tamaño anteriores no cambiarán eso. En estos casos, el cambio de tamaño no funcionará correctamente.

En el código a continuación, la escala se establece en 1 (sin escala) y la image devuelta tiene el tamaño que cabe esperar. Esto se hace en la llamada UIGraphicsBeginImageContextWithOptions .

 -(UIImage *)resizeImage :(UIImage *)theImage :(CGSize)theNewSize { UIGraphicsBeginImageContextWithOptions(theNewSize, NO, 1.0); [theImage drawInRect:CGRectMake(0, 0, theNewSize.width, theNewSize.height)]; UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newImage; } 

Terminé usando la técnica de Brads para crear un método scaleToFitWidth en UIImage+Extensions si eso es útil para cualquiera …

 -(UIImage *)scaleToFitWidth:(CGFloat)width { CGFloat ratio = width / self.size.width; CGFloat height = self.size.height * ratio; NSLog(@"W:%f H:%f",width,height); UIGraphicsBeginImageContext(CGSizeMake(width, height)); [self drawInRect:CGRectMake(0.0f,0.0f,width,height)]; UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newImage; } 

luego donde sea que te gusta

#import "UIImage+Extensions.h"

UIImage *newImage = [image scaleToFitWidth:100.0f];

También vale la pena señalar que podría mover esto más abajo en una class de UIView+Extensions si desea representar imágenes desde una UIView

Solo quería responder a esa pregunta para los progtwigdores de Cocoa Swift. Esta function devuelve NSImage con un nuevo tamaño. Puedes usar esa function como esta.

  let sizeChangedImage = changeImageSize(image, ratio: 2) // changes image size func changeImageSize (image: NSImage, ratio: CGFloat) -> NSImage { // getting the current image size let w = image.size.width let h = image.size.height // calculating new size let w_new = w / ratio let h_new = h / ratio // creating size constant let newSize = CGSizeMake(w_new ,h_new) //creating rect let rect = NSMakeRect(0, 0, w_new, h_new) // creating a image context with new size let newImage = NSImage.init(size:newSize) newImage.lockFocus() // drawing image with new size in context image.drawInRect(rect) newImage.unlockFocus() return newImage } 

Prueba esto, funciona para mí,

  CGRect rect =CGRectMake (0, 0,1200,900); UIGraphicsBeginImageContext( rect.size ); [image drawInRect:rect]; UIImage *picture1 = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); NSData *imageData = UIImagePNGRepresentation(picture1); UIImage *img=[UIImage imageWithData:imageData]; [imageArray addObject:img]; 
 - (UIImage *)resizeImage:(UIImage*)image newSize:(CGSize)newSize { CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height)); CGImageRef imageRef = image.CGImage; UIGraphicsBeginImageContextWithOptions(newSize, NO, 0); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetInterpolationQuality(context, kCGInterpolationHigh); CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height); CGContextConcatCTM(context, flipVertical); CGContextDrawImage(context, newRect, imageRef); CGImageRef newImageRef = CGBitmapContextCreateImage(context); UIImage *newImage = [UIImage imageWithCGImage:newImageRef]; CGImageRelease(newImageRef); UIGraphicsEndImageContext(); return newImage; } 

Agregando a la gran cantidad de respuestas aquí, pero he optado por una solución que se networkingimensiona por tamaño de file, en lugar de dimensiones.

Esto networkingucirá las dimensiones y la calidad de la image hasta que scope su tamaño determinado.

 func compressTo(toSizeInMB size: Double) -> UIImage? { let bytes = size * 1024 * 1024 let sizeInBytes = Int(bytes) var needCompress:Bool = true var imgData:Data? var compressingValue:CGFloat = 1.0 while (needCompress) { if let resizedImage = scaleImage(byMultiplicationFactorOf: compressingValue), let data: Data = UIImageJPEGRepresentation(resizedImage, compressingValue) { if data.count < sizeInBytes || compressingValue < 0.1 { needCompress = false imgData = data } else { compressingValue -= 0.1 } } } if let data = imgData { print("Finished with compression value of: \(compressingValue)") return UIImage(data: data) } return nil } private func scaleImage(byMultiplicationFactorOf factor: CGFloat) -> UIImage? { let size = CGSize(width: self.size.width*factor, height: self.size.height*factor) UIGraphicsBeginImageContext(size) draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height)) if let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext() { UIGraphicsEndImageContext() return newImage; } return nil } 

Crédito por escalamiento por tamaño de respuesta

Para cambiar el tamaño de una image, tengo mejores resultados (charts) al usar esta function en lugar de DrawInRect:

 - (UIImage*) networkinguceImageSize:(UIImage*) pImage newwidth:(float) pWidth { float lScale = pWidth / pImage.size.width; CGImageRef cgImage = pImage.CGImage; UIImage *lResult = [UIImage imageWithCGImage:cgImage scale:lScale orientation:UIImageOrientationRight]; return lResult; } 

La relación de aspecto se cuida automáticamente