Meine Anwendung lädt eine Reihe von Bilddateien aus dem Netzwerk herunter und speichert sie auf der lokalen iPhone-Festplatte. Einige dieser Bilder sind ziemlich groß (z. B. Breiten größer als 500 Pixel). Da das iPhone nicht einmal über eine ausreichend große Anzeige verfügt, um das Bild in seiner ursprünglichen Größe darzustellen, plane ich, das Bild auf etwas kleiner zu skalieren, um Platz und Leistung zu sparen.
Bei einigen dieser Bilder handelt es sich ebenfalls um JPEG-Bilder, die nicht in der üblichen Einstellung für 60% Qualität gespeichert werden.
Wie kann ich die Größe eines Bildes mit dem iPhone SDK ändern und wie kann ich die Qualitätseinstellung eines JPEG-Bildes ändern?
Ein paar Vorschläge werden als Antwort auf diese Frage gegeben. Ich hatte die in this post beschriebene Technik mit dem entsprechenden Code vorgeschlagen:
+ (UIImage*)imageWithImage:(UIImage*)image
scaledToSize:(CGSize)newSize;
{
UIGraphicsBeginImageContext( newSize );
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Was das Speichern des Bildes anbelangt, so ist PNG das schnellste Bildformat, das mit dem iPhone verwendet werden kann, da es für dieses Format Optimierungen aufweist. Wenn Sie diese Bilder jedoch als JPEG-Dateien speichern möchten, können Sie Ihre UIImage wie folgt verwenden:
NSData *dataForJPEGFile = UIImageJPEGRepresentation(theImage, 0.6);
Dadurch wird eine NSData-Instanz erstellt, die die Rohbytes für ein JPEG-Bild mit einer Qualitätseinstellung von 60% enthält. Der Inhalt dieser NSData-Instanz kann dann auf die Festplatte geschrieben oder im Arbeitsspeicher zwischengespeichert werden.
Dies ist der einfachste und einfachste Weg, um die Größe Ihrer Bilder zu ändern
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();
Die oben genannten Methoden funktionieren gut für kleine Bilder. Wenn Sie jedoch versuchen, die Größe eines sehr großen Bildes zu ändern, wird der Speicher schnell aufgebraucht und die App zum Absturz gebracht. Ein viel besserer Weg ist, CGImageSourceCreateThumbnailAtIndex
zu verwenden, um die Größe des Bildes zu ändern, ohne es vorher vollständig dekodieren zu müssen.
Wenn Sie den Pfad zu dem Bild haben, dessen Größe Sie ändern möchten, können Sie Folgendes verwenden:
- (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);
}
Weitere Details hier .
Die beste Möglichkeit, Bilder zu skalieren, ohne das Seitenverhältnis zu verlieren (d. H. Ohne das Bild zu dehnen), ist die Verwendung dieser Methode:
//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;
}
Fügen Sie diese Methode Ihrer Utility-Klasse hinzu, damit Sie sie im gesamten Projekt verwenden können und auf diese Weise wie folgt zugreifen:
xyzImageView.image = [Utility scaleImage:yourUIImage toSize:xyzImageView.frame.size];
Diese Methode sorgt für die Skalierung, während das Seitenverhältnis beibehalten wird ..__ Sie fügt dem Bild außerdem Einrückungen hinzu, falls das verkleinerte Bild mehr Breite als Höhe hat (oder umgekehrt).
Wenn Sie die Kontrolle über den Server haben, würde ich dringend empfehlen, die Imageserverseite mit ImageMagik zu ändern. Das Herunterladen großer Bilder und deren Größenänderung am Telefon ist eine Verschwendung vieler kostbarer Ressourcen - Bandbreite, Batterie und Speicher. All dies ist auf Handys knapp.
Ich habe eine ultimative Lösung für die Bildskalierung in Swift entwickelt.
Sie können es verwenden, um die Größe des Bildes auf die angegebene Größe zu ändern.
Sie können das Bild auf die Mitte oder auf vier Kanten und vier Ecken ausrichten.
Sie können auch zusätzlichen Platz zuschneiden, der hinzugefügt wird, wenn die Seitenverhältnisse von Originalbild und Zielgröße nicht gleich sind.
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
}
}
Nachfolgend finden Sie Beispiele für die Anwendung dieser Lösung.
Graues Rechteck ist das Ziel-Site-Bild wird in ... verkleinert. Blaue Kreise in hellblauem Rechteck sind das Bild (ich habe Kreise verwendet, da es leicht zu sehen ist, wenn es skaliert wird, ohne dass der Aspekt erhalten bleibt) wird abgeschnitten, wenn Sie trim: true
übergeben.
Aspect Fit vor und nach der Skalierung:
Ein weiteres Beispiel für aspect fit:
Aspect Fit mit oberer Ausrichtung:
Aspektfüllung:
Füllen:
Ich habe in meinen Beispielen das Upscaling verwendet, weil es einfacher zu demonstrieren ist, aber die Lösung auch für das Downscaling funktioniert.
Für die JPEG-Komprimierung sollten Sie Folgendes verwenden:
let compressionQuality: CGFloat = 0.75 // adjust to change JPEG quality
if let data = UIImageJPEGRepresentation(image, compressionQuality) {
// ...
}
Sie können meine Gist mit Xcode-Spielplatz ausprobieren.
Bei Swift 3 skaliert der folgende Code das Bild unter Beibehaltung des Seitenverhältnisses. Weitere Informationen zum ImageContext finden Sie in Apples Dokumentation :
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!
}
}
Rufen Sie zur Verwendung die resizeImage()
-Methode auf:
UIImage.resizeImage(image: yourImageName, newHeight: yourImageNewHeight)
sie können diesen Code verwenden, um das Bild in der erforderlichen Größe zu skalieren.
+ (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;
}
Ich füge noch mehr Antworten hinzu, aber ich habe mich für eine Lösung entschieden, deren Größe sich nach Dateigröße und nicht nach Abmessungen richtet.
Dadurch werden sowohl die Abmessungen als auch die Qualität des Bildes reduziert, bis es Ihre vorgegebene Größe erreicht.
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
}
Gutschrift für Skalierung nach Größenantwort
Am Ende habe ich die Brads-Technik benutzt, um eine scaleToFitWidth
-Methode in UIImage+Extensions
zu erstellen, falls dies für jeden nützlich ist ...
-(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;
}
dann wo immer du magst
#import "UIImage+Extensions.h"
UIImage *newImage = [image scaleToFitWidth:100.0f];
Beachten Sie auch, dass Sie dies weiter unten in eine UIView+Extensions
-Klasse verschieben können, wenn Sie Bilder aus einer UIView rendern möchten
Wenn sich das Bild im Dokumentverzeichnis befindet, fügen Sie dieseURLextension hinzu:
extension URL {
func compressedImageURL(quality: CGFloat = 0.3) throws -> URL? {
let imageData = try Data(contentsOf: self)
debugPrint("Image file size before compression: \(imageData.count) bytes")
let compressedURL = NSURL.fileURL(withPath: NSTemporaryDirectory() + NSUUID().uuidString + ".jpg")
guard let actualImage = UIImage(data: imageData) else { return nil }
guard let compressedImageData = UIImageJPEGRepresentation(actualImage, quality) else {
return nil
}
debugPrint("Image file size after compression: \(compressedImageData.count) bytes")
do {
try compressedImageData.write(to: compressedURL)
return compressedURL
} catch {
return nil
}
}
}
Verwendungszweck:
guard let localImageURL = URL(string: "< LocalImagePath.jpg >") else {
return
}
//Here you will get URL of compressed image
guard let compressedImageURL = try localImageURL.compressedImageURL() else {
return
}
debugPrint("compressedImageURL: \(compressedImageURL.absoluteString)")
Hinweis: - Ändern Sie <LocalImagePath.jpg> mit Ihrem lokalen JPG-Bildpfad.
func resizeImage(image: UIImage, newWidth: CGFloat) -> UIImage? {
let scale = newWidth / image.size.width
let newHeight = CGFloat(200.0)
UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
image.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
Ich wollte diese Frage nur für Cocoa Swift-Programmierer beantworten. Diese Funktion gibt NSImage mit neuer Größe zurück. Sie können diese Funktion so verwenden.
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
}
Ein Problem, das bei Retina-Displays auftreten kann, besteht darin, dass die Bildskala von ImageCapture oder dergleichen festgelegt wird. Die Größenänderungsfunktionen oben ändern daran nichts. In diesen Fällen funktioniert die Größenänderung nicht richtig.
Im folgenden Code ist der Maßstab auf 1 gesetzt (nicht skaliert) und das zurückgegebene Bild hat die erwartete Größe. Dies geschieht im Aufruf 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;
}