Ein UIImage beschneiden


209

Ich habe einen Code, der die Größe eines Bildes ändert, damit ich einen skalierten Teil der Bildmitte erhalten kann. Ich verwende diesen Code, UIImageum eine kleine, quadratische Darstellung eines Bildes aufzunehmen und zurückzugeben, ähnlich wie in der Albumansicht von die Fotos App. (Ich weiß, ich könnte a verwenden UIImageViewund den Zuschneidemodus anpassen, um die gleichen Ergebnisse zu erzielen, aber diese Bilder werden manchmal in angezeigt UIWebViews).

Ich habe angefangen, einige Abstürze in diesem Code zu bemerken und bin ein bisschen ratlos. Ich habe zwei verschiedene Theorien und frage mich, ob beide auf der Basis sind.

Theorie 1) Ich erreiche das Zuschneiden, indem ich in einen Offscreen-Bildkontext meiner Zielgröße zeichne. Da ich den mittleren Teil des Bildes haben möchte, setze ich das CGRectArgument an drawInRectetwas, das größer ist als die Grenzen meines Bildkontexts. Ich hatte gehofft, dass dies koscher ist, aber versuche ich stattdessen, andere Erinnerungen zu erfassen, die ich nicht berühren sollte?

Theorie 2) Ich mache das alles in einem Hintergrund-Thread. Ich weiß, dass es Teile von UIKit gibt, die auf den Haupt-Thread beschränkt sind. Ich ging davon aus / hoffte, dass das Zeichnen in eine Offscreen-Ansicht nicht dazu gehört. Liege ich falsch?

(Oh, wie ich NSImage's drawInRect:fromRect:operation:fraction:Methode vermisse .)


Wenn Sie versuchen, einen Absturz zu diagnostizieren, sollten Sie die App unter dem Debugger ausführen und notieren, was passiert, wenn sie abstürzt. Sie haben noch nicht einmal festgestellt, ob eine Ausnahme ausgelöst wird oder ob EXC_BAD_ACCESS aufgrund eines baumelnden Zeigers angezeigt wird. Sobald Sie das wissen, können Sie anfangen, Theorien zu machen.
Benzado

Meinetwegen. Ich habe dies unter dem Debugger nicht erneut angegeben, obwohl ich EXC_BAD_ACCESS-Meldungen im Absturzprotokoll habe. Als ich dies veröffentlichte, arbeitete ich unter der Annahme, dass ich einen dummen Fehler in meiner Implementierung gemacht hatte und jemand darauf springen würde (wie das Vergessen eines Beschneidungspfads).
Jablair

Ungeachtet dessen, was es wert ist und gute Antworten, gibt es auf NSHipster eine gute Übersicht über Techniken und deren Leistung: nshipster.com/image-resizing . Der Purist in mir wollte CIImage verwenden, aber der Pragmatiker entschied sich für UIKit / Bildkontext.
Chris Conover

Antworten:


236

Update 28.05.2014: Ich habe dies geschrieben, als iOS 3 oder so das heiße neue Ding war. Ich bin mir sicher, dass es jetzt bessere Möglichkeiten gibt, dies zu tun, möglicherweise eingebaut. Wie viele Leute erwähnt haben, berücksichtigt diese Methode keine Rotation; Lesen Sie einige zusätzliche Antworten und verbreiten Sie ein wenig Liebe, um die Antworten auf diese Frage für alle hilfreich zu halten.

Ursprüngliche Antwort:

Ich werde meine Antwort auf dieselbe Frage an anderer Stelle kopieren / einfügen:

Es gibt keine einfache Klassenmethode, um dies zu tun, aber es gibt eine Funktion, mit der Sie die gewünschten Ergebnisse erzielen können: Sie CGImageCreateWithImageInRect(CGImageRef, CGRect)wird Ihnen helfen.

Hier ist ein kurzes Beispiel:

CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect);
// or use the UIImage wherever you like
[UIImageView setImage:[UIImage imageWithCGImage:imageRef]]; 
CGImageRelease(imageRef);

13
Wenn das imageOrientationauf etwas anderes als nach oben eingestellt ist, wird das CGImagein Bezug auf das gedreht UIImageund das zugeschnittene Rechteck ist falsch. Sie können das Beschneidungsrechteck entsprechend drehen, aber die frisch erstellten UIImagehaben imageOrientation, so dass die beschnittene CGImageInneren gedreht wird.
Zoul

25
Ich möchte darauf hinweisen, dass Sie auf dem Retina-Display Ihre CropRect-Breite und -Höhe mit dieser Methode verdoppeln müssen. Nur etwas, auf das ich gestoßen bin.
Petrocket

21
Damit es in jeder Ausrichtung funktioniert, verwenden Sie:[UIImage imageWithCGImage:imageRef scale:largeImage.scale orientation:largeImage.imageOrientation];
Nicos Karalis

3
Sie sollten Unterstützung für Retina hinzufügen !!
Mário Carvalho

1
@NicosKaralis, der es nur in die richtige Ausrichtung dreht. Der Bereich, den Sie aus dem Bild herausschneiden, ist jedoch immer noch falsch.
Tim Bodeit

90

Verwenden Sie die folgende Methode in einer UIImage-Kategorie (iOS 4.0 und höher), um Retina-Bilder bei gleichem Maßstab und gleicher Ausrichtung zuzuschneiden:

- (UIImage *)crop:(CGRect)rect {
    if (self.scale > 1.0f) {
        rect = CGRectMake(rect.origin.x * self.scale,
                          rect.origin.y * self.scale,
                          rect.size.width * self.scale,
                          rect.size.height * self.scale);
    }

    CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(imageRef);
    return result;
}

14
Sie könnten einfach die Bedingung hier töten und immer mehrere vorbei self.scale, nein?
Michael Mior

2
Sie haben Recht, Michael, aber ich denke, die obige Lösung ist auf Geräten ohne Netzhaut etwas schneller, da sie nur eine einzige Überprüfung anstelle von vier Multiplikationen plus einer Zuordnung durchführt. Bei Retina-Geräten ist es nur ein einziger Boolescher Check mehr als nötig, es ist also eine Frage der persönlichen Präferenz oder der Zielanpassung.
CGee

2
Ist es richtig, CGImageRelease(imageRef);mit aktiviertem ARC zu verwenden ?
CGee


3
Tatsächlich sind 4 Multiplikationen möglicherweise schneller als die Bewertung einer Bedingung :) Multiplikationen können auf modernen CPUs ziemlich gut optimiert werden, während Bedingungen nur vorhergesagt werden können. Aber Sie haben Recht, es ist eine Frage der Präferenz. Ich bevorzuge den kürzeren / einfacheren Code wegen der verbesserten Lesbarkeit.
Marsbear

65

Sie können eine UIImage-Kategorie erstellen und überall dort verwenden, wo Sie sie benötigen. Basierend auf der Antwort von HitScans und den Kommentaren unten.

@implementation UIImage (Crop)

- (UIImage *)crop:(CGRect)rect {

    rect = CGRectMake(rect.origin.x*self.scale, 
                      rect.origin.y*self.scale, 
                      rect.size.width*self.scale, 
                      rect.size.height*self.scale);       

    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], rect);
    UIImage *result = [UIImage imageWithCGImage:imageRef 
                                          scale:self.scale 
                                    orientation:self.imageOrientation]; 
    CGImageRelease(imageRef);
    return result;
}

@end

Sie können es folgendermaßen verwenden:

UIImage *imageToCrop = <yourImageToCrop>;
CGRect cropRect = <areaYouWantToCrop>;   

//for example
//CGRectMake(0, 40, 320, 100);

UIImage *croppedImage = [imageToCrop crop:cropRect];

3
Sollte nicht [[UIScreen mainScreen] scale]einfach so sein self.scale? Der Maßstab des Bildes stimmt möglicherweise nicht mit dem Maßstab des Bildschirms überein.
Michael Mior

Hallo, ich habe Ihre Antwort ausprobiert und es gibt mir No visible @interface for 'UIImage' declares the selector 'crop', obwohl ich die Kategoriedateien .h und .m in mein Projekt eingefügt und die .h in die Klasse importiert habe, in der ich die Kategorie verwende. Irgendeine Idee?
Ali

Behoben. Ich habe den Methodenheader nicht in die Datei UIImage + Crop.h eingefügt.
Ali

Ich möchte das Bild in Kreisform zuschneiden. Damit wir nur Kreisbahn sehen können und andere Wege transparent bleiben.
Alfa

Dann können Sie CGImageCreateWithMask oder CGImageCreateWithMaskingColors anstelle von CGImageCreateWithImageInRect
Vilém Kurz

54

Swift 3 Version

func cropImage(imageToCrop:UIImage, toRect rect:CGRect) -> UIImage{
    
    let imageRef:CGImage = imageToCrop.cgImage!.cropping(to: rect)!
    let cropped:UIImage = UIImage(cgImage:imageRef)
    return cropped
}


let imageTop:UIImage  = UIImage(named:"one.jpg")! // add validation

Geben Sie hier die Bildbeschreibung ein

mit Hilfe dieser Brückenfunktion CGRectMake-> CGRect(Credits zu dieser Antwort beantwortet von @rob mayoff):

 func CGRectMake(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> CGRect {
    return CGRect(x: x, y: y, width: width, height: height)
}

Die Verwendung ist:

if var image:UIImage  = UIImage(named:"one.jpg"){
   let  croppedImage = cropImage(imageToCrop: image, toRect: CGRectMake(
        image.size.width/4,
        0,
        image.size.width/2,
        image.size.height)
    )
}

Ausgabe:

Geben Sie hier die Bildbeschreibung ein


Ich bin mir nicht sicher, warum es abgelehnt wurde, ich fand es eine gute Antwort. Das einzige, woran ich denken kann, ist, dass es nicht skaliert werden kann, sodass es nicht funktioniert, wenn Sie @ 2x / @ 3x-Bilder haben und Ihre Funktionsnamen nicht übereinstimmen.
William T.

4
Sie müssen in guarddie erste Zeile gehen, falls UIImage.CGImagezurückgegeben wird nilund warum verwendet wird, varwenn es letperfekt funktioniert.
NoodleOfDeath

Dies ist jedoch richtig. Lesen Sie die Dokumente jedoch cropping(to:)sehr sorgfältig durch. Das Ergebnis CGImageenthält einen starken Bezug zu dem größeren, CGImagevon dem es abgeschnitten wurde. Wenn Sie also nur am zugeschnittenen Bild festhalten möchten und kein Original benötigen, schauen Sie sich die anderen Lösungen an.
Jsadler

Dies funktioniert nicht mit Bildern mit Orientierungs-Tags. Der Zuschneidebereich wird nicht richtig gedreht.
Max

1
@Max Dies liegt daran, dass cgImage keine Orientierungsunterstützung bietet. Wenn UIImage hat, rightsollte das cgImage um 90 Grad gedreht werden, daher sollte auch das Crop Rect gedreht werden
MK Yung

49

Hier ist meine UIImage-Crop-Implementierung, die der imageOrientation-Eigenschaft folgt. Alle Orientierungen wurden gründlich getestet.

inline double rad(double deg)
{
    return deg / 180.0 * M_PI;
}

UIImage* UIImageCrop(UIImage* img, CGRect rect)
{
    CGAffineTransform rectTransform;
    switch (img.imageOrientation)
    {
        case UIImageOrientationLeft:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -img.size.height);
            break;
        case UIImageOrientationRight:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -img.size.width, 0);
            break;
        case UIImageOrientationDown:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -img.size.width, -img.size.height);
            break;
        default:
            rectTransform = CGAffineTransformIdentity;
    };
    rectTransform = CGAffineTransformScale(rectTransform, img.scale, img.scale);

    CGImageRef imageRef = CGImageCreateWithImageInRect([img CGImage], CGRectApplyAffineTransform(rect, rectTransform));
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:img.scale orientation:img.imageOrientation];
    CGImageRelease(imageRef);
    return result;
}

7
Warnung implicit declaration of function 'rad' is invalid in c99kann durch Ersetzen entfernt werden: rad (90), rad (-90), rad (-180) -> M_PI_2, -M_PI_2, -_M_PI
Sound Blaster

Ups, tut mir Leid. Die Dienstprogrammfunktion rad () wurde zum Quell-Snippet hinzugefügt.
Sergii Rudchenko

contains undefined reference for architecture armv7Vermisse ich eine Bibliothek? CoreGraphics wird importiert.
Bob Spryn

1
@SergiiRudchenko "Alle Orientierungen wurden gründlich getestet." - Beinhaltet dies die gespiegelten Ausrichtungen?
Tim Bodeit

@ BobSpryn Nein, Sie vermissen keine Bibliothek. Obwohl ich nicht erklären kann, was der Fehler bedeutet, behebt das Ersetzen von rad (), wie von SoundBlaster vorgeschlagen, auch diesen Fehler.
Tim Bodeit

39

Heads up: Alle diese Antworten setzen ein CGImageBildobjekt mit Unterstützung voraus.

image.CGImagekann nil zurückgeben, wenn das UIImagevon a unterstützt wird CIImage, was der Fall wäre, wenn Sie dieses Bild mit a erstellen würden CIFilter.

In diesem Fall müssen Sie das Bild möglicherweise in einem neuen Kontext zeichnen und das Bild zurückgeben ( langsam ).

UIImage* crop(UIImage *image, rect) {
    UIGraphicsBeginImageContextWithOptions(rect.size, false, [image scale]);
    [image drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
    cropped_image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return cropped_image;
}

5
Dies ist genau das, was ich brauchte, funktioniert in allen Szenarien daher besser als jede andere Lösung!
David Thompson

2
image.size in der ersten Zeile sollte rect.size sein UIGraphicsBeginImageContextWithOptions (rect.size, false, image.scale);
Brett

2
Danke Brett, definitiv genau dort. Ich habe den Code aktualisiert, um dieses Update einzuschließen.
Colinta

Warum ist es -rect.origin.x nicht nur rect.origin.x?
Aggressor

2
Weil wir beschneiden; Mit anderen Worten, das Argument 'rect' sagt: "Ich möchte, dass das obere linke Bild des neuen Bildes beispielsweise bei [10, 10] beginnt." Dazu zeichnen wir das Bild so, dass [10, 10] am Ursprung des neuen Bildes liegt.
Colinta

35

Keine der Antworten hier behandelt alle Skalierungs- und Rotationsprobleme zu 100% korrekt. Hier ist eine Synthese von allem, was bisher gesagt wurde und ab iOS7 / 8 aktuell ist. Es soll als Methode in eine Kategorie auf UIImage aufgenommen werden.

- (UIImage *)croppedImageInRect:(CGRect)rect
{
    double (^rad)(double) = ^(double deg) {
        return deg / 180.0 * M_PI;
    };

    CGAffineTransform rectTransform;
    switch (self.imageOrientation) {
        case UIImageOrientationLeft:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -self.size.height);
            break;
        case UIImageOrientationRight:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -self.size.width, 0);
            break;
        case UIImageOrientationDown:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -self.size.width, -self.size.height);
            break;
        default:
            rectTransform = CGAffineTransformIdentity;
    };
    rectTransform = CGAffineTransformScale(rectTransform, self.scale, self.scale);

    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], CGRectApplyAffineTransform(rect, rectTransform));
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(imageRef);

    return result;
}

1
Warum wird diese Antwort nicht mehr positiv bewertet? Ich habe alle oben genannten Antworten ausprobiert und bin auf unzählige Probleme gestoßen (insbesondere die Konvertierung von UIImage zu CIImage und der Verlust der richtigen Transformationsdaten). Dies ist die einzige, die für mich funktioniert!
Aggressor

1
Danke @Aggressor, ich bin froh, dass das für dich funktioniert. Meine Antwort wurde erst vor einem Monat veröffentlicht, während viele dieser Antworten seit 5 Jahren hier sind. Ich vermute, das erklärt das Fehlen vergleichbarer Stimmen.
Awolf

Ich verwende dies, um Filter über Bilder zu streichen (wie dies bei Snapchat der Fall ist), und es treten leichte Skalierungsprobleme auf. Gibt es irgendetwas in Ihrem Code, das Sie vorschlagen können, um die Ursache für diesen Skalierungsfehler zu ermitteln? Ich habe meine Lösung, die Ihren Code verwendet, hier veröffentlicht: stackoverflow.com/questions/23319497/…
Aggressor

eine sehr nützliche Funktion, danke. Gibt es Tipps, was geändert werden sollte, um den Schwerpunkt auf den zentralen Teil des Bildes zu legen?
Privat

Dies sollte die Antwort sein.
Hufeisen7

17

Schnelle Version von awolf's Antwort, die für mich funktioniert hat:

public extension UIImage {
    func croppedImage(inRect rect: CGRect) -> UIImage {
        let rad: (Double) -> CGFloat = { deg in
            return CGFloat(deg / 180.0 * .pi)
        }
        var rectTransform: CGAffineTransform
        switch imageOrientation {
        case .left:
            let rotation = CGAffineTransform(rotationAngle: rad(90))
            rectTransform = rotation.translatedBy(x: 0, y: -size.height)
        case .right:
            let rotation = CGAffineTransform(rotationAngle: rad(-90))
            rectTransform = rotation.translatedBy(x: -size.width, y: 0)
        case .down:
            let rotation = CGAffineTransform(rotationAngle: rad(-180))
            rectTransform = rotation.translatedBy(x: -size.width, y: -size.height)
        default:
            rectTransform = .identity
        }
        rectTransform = rectTransform.scaledBy(x: scale, y: scale)
        let transformedRect = rect.applying(rectTransform)
        let imageRef = cgImage!.cropping(to: transformedRect)!
        let result = UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation)
        return result
    }
}

10
CGSize size = [originalImage size];
int padding = 20;
int pictureSize = 300;
int startCroppingPosition = 100;
if (size.height > size.width) {
    pictureSize = size.width - (2.0 * padding);
    startCroppingPosition = (size.height - pictureSize) / 2.0; 
} else {
    pictureSize = size.height - (2.0 * padding);
    startCroppingPosition = (size.width - pictureSize) / 2.0;
}
// WTF: Don't forget that the CGImageCreateWithImageInRect believes that 
// the image is 180 rotated, so x and y are inverted, same for height and width.
CGRect cropRect = CGRectMake(startCroppingPosition, padding, pictureSize, pictureSize);
CGImageRef imageRef = CGImageCreateWithImageInRect([originalImage CGImage], cropRect);
UIImage *newImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:originalImage.imageOrientation];
[m_photoView setImage:newImage];
CGImageRelease(imageRef);

Die meisten Antworten, die ich gesehen habe, befassen sich nur mit einer Position von (0, 0) für (x, y). Ok, das ist ein Fall, aber ich möchte, dass mein Zuschneidevorgang zentriert wird. Was ich eine Weile gebraucht habe, um herauszufinden, ist die Zeile nach dem WTF-Kommentar.

Nehmen wir den Fall eines Bildes, das im Hochformat aufgenommen wurde:

  1. Die ursprüngliche Bildhöhe ist höher als die Breite (Woo, bisher keine Überraschung!)
  2. Das Bild, das sich die CGImageCreateWithImageInRect-Methode in ihrer eigenen Welt vorstellt, ist jedoch kein Porträt, sondern eine Landschaft (wenn Sie das Orientierungsargument im imageWithCGImage-Konstruktor nicht verwenden, wird es als 180 gedreht angezeigt).
  3. Sie sollten sich also vorstellen, dass es sich um eine Landschaft handelt, wobei die Position (0, 0) die obere rechte Ecke des Bildes ist.

Hoffe es macht Sinn! Wenn dies nicht der Fall ist, probieren Sie verschiedene Werte aus. Sie werden feststellen, dass die Logik invertiert ist, wenn Sie das richtige x, y, die richtige Breite und Höhe für Ihr cropRect auswählen.


8

swift3

extension UIImage {
    func crop(rect: CGRect) -> UIImage? {
        var scaledRect = rect
        scaledRect.origin.x *= scale
        scaledRect.origin.y *= scale
        scaledRect.size.width *= scale
        scaledRect.size.height *= scale
        guard let imageRef: CGImage = cgImage?.cropping(to: scaledRect) else {
            return nil
        }
        return UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation)
    }
}

7

Schnelle Erweiterung

extension UIImage {
    func crop(var rect: CGRect) -> UIImage {
        rect.origin.x*=self.scale
        rect.origin.y*=self.scale
        rect.size.width*=self.scale
        rect.size.height*=self.scale

        let imageRef = CGImageCreateWithImageInRect(self.CGImage, rect)
        let image = UIImage(CGImage: imageRef, scale: self.scale, orientation: self.imageOrientation)!
        return image
    }
}

4

Beste Lösung zum Zuschneiden eines UIImage in Swift , in Bezug auf Genauigkeit, Pixel-Skalierung ...:

private func squareCropImageToSideLength(let sourceImage: UIImage,
    let sideLength: CGFloat) -> UIImage {
        // input size comes from image
        let inputSize: CGSize = sourceImage.size

        // round up side length to avoid fractional output size
        let sideLength: CGFloat = ceil(sideLength)

        // output size has sideLength for both dimensions
        let outputSize: CGSize = CGSizeMake(sideLength, sideLength)

        // calculate scale so that smaller dimension fits sideLength
        let scale: CGFloat = max(sideLength / inputSize.width,
            sideLength / inputSize.height)

        // scaling the image with this scale results in this output size
        let scaledInputSize: CGSize = CGSizeMake(inputSize.width * scale,
            inputSize.height * scale)

        // determine point in center of "canvas"
        let center: CGPoint = CGPointMake(outputSize.width/2.0,
            outputSize.height/2.0)

        // calculate drawing rect relative to output Size
        let outputRect: CGRect = CGRectMake(center.x - scaledInputSize.width/2.0,
            center.y - scaledInputSize.height/2.0,
            scaledInputSize.width,
            scaledInputSize.height)

        // begin a new bitmap context, scale 0 takes display scale
        UIGraphicsBeginImageContextWithOptions(outputSize, true, 0)

        // optional: set the interpolation quality.
        // For this you need to grab the underlying CGContext
        let ctx: CGContextRef = UIGraphicsGetCurrentContext()
        CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh)

        // draw the source image into the calculated rect
        sourceImage.drawInRect(outputRect)

        // create new image from bitmap context
        let outImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()

        // clean up
        UIGraphicsEndImageContext()

        // pass back new image
        return outImage
}

Anweisungen zum Aufrufen dieser Funktion:

let image: UIImage = UIImage(named: "Image.jpg")!
let squareImage: UIImage = self.squareCropImageToSideLength(image, sideLength: 320)
self.myUIImageView.image = squareImage

Hinweis: Die erste in Objective-C geschriebene Inspiration für den Quellcode wurde im Blog "Cocoanetics" gefunden.


3

Das folgende Code-Snippet könnte helfen.

import UIKit

extension UIImage {
    func cropImage(toRect rect: CGRect) -> UIImage? {
        if let imageRef = self.cgImage?.cropping(to: rect) {
            return UIImage(cgImage: imageRef)
        }
        return nil
    }
}

2

Sieht etwas seltsam aus, funktioniert aber hervorragend und berücksichtigt die Bildorientierung:

var image:UIImage = ...

let img = CIImage(image: image)!.imageByCroppingToRect(rect)
image = UIImage(CIImage: img, scale: 1, orientation: image.imageOrientation)

1
aber es bricht offensichtlich die Skala
Sulthan

2

Swift 5:

extension UIImage {
    func cropped(rect: CGRect) -> UIImage? {
        guard let cgImage = cgImage else { return nil }

        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
        let context = UIGraphicsGetCurrentContext()

        context?.translateBy(x: 0.0, y: self.size.height)
        context?.scaleBy(x: 1.0, y: -1.0)
        context?.draw(cgImage, in: CGRect(x: rect.minX, y: rect.minY, width: self.size.width, height: self.size.height), byTiling: false)


        let croppedImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return croppedImage
    }
}

1
- (UIImage *)getSubImage:(CGRect) rect{
    CGImageRef subImageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
    CGRect smallBounds = CGRectMake(rect.origin.x, rect.origin.y, CGImageGetWidth(subImageRef), CGImageGetHeight(subImageRef));

    UIGraphicsBeginImageContext(smallBounds.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextDrawImage(context, smallBounds, subImageRef);
    UIImage* smallImg = [UIImage imageWithCGImage:subImageRef];
    UIGraphicsEndImageContext();

    return smallImg;
}

1
 (UIImage *)squareImageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
    double ratio;
    double delta;
    CGPoint offset;

    //make a new square size, that is the resized imaged width
    CGSize sz = CGSizeMake(newSize.width, newSize.width);

    //figure out if the picture is landscape or portrait, then
    //calculate scale factor and offset
    if (image.size.width > image.size.height) {
        ratio = newSize.width / image.size.width;
        delta = (ratio*image.size.width - ratio*image.size.height);
        offset = CGPointMake(delta/2, 0);
    } else {
        ratio = newSize.width / image.size.height;
        delta = (ratio*image.size.height - ratio*image.size.width);
        offset = CGPointMake(0, delta/2);
    }

    //make the final clipping rect based on the calculated values
    CGRect clipRect = CGRectMake(-offset.x, -offset.y,
                                 (ratio * image.size.width) + delta,
                                 (ratio * image.size.height) + delta);


    //start a new context, with scale factor 0.0 so retina displays get
    //high quality image
    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
        UIGraphicsBeginImageContextWithOptions(sz, YES, 0.0);
    } else {
        UIGraphicsBeginImageContext(sz);
    }
    UIRectClip(clipRect);
    [image drawInRect:clipRect];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}

1

Unter iOS9.2SDK verwende ich die folgende Methode, um Frames von UIView in UIImage zu konvertieren

-(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect
{
  CGSize cropSize = rect.size;
  CGFloat widthScale = image.size.width/self.imageViewOriginal.bounds.size.width;
  CGFloat heightScale = image.size.height/self.imageViewOriginal.bounds.size.height;
  cropSize = CGSizeMake(rect.size.width*widthScale, 
              rect.size.height*heightScale);
  CGPoint pointCrop = CGPointMake(rect.origin.x*widthScale,
             rect.origin.y*heightScale);
  rect = CGRectMake(pointCrop.x, pointCrop.y, cropSize.width, cropSize.height);
  CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect);
  UIImage *croppedImage = [UIImage imageWithCGImage:subImage];
  CGImageRelease(subImage);

  return croppedImage;
}

1

Swift 2.0 Update ( CIImageKompatibilität)

Erweitern von Maxim's Answer , funktioniert aber auch, wenn Ihr Bild CIImagebasiert.

public extension UIImage {
    func imageByCroppingToRect(rect: CGRect) -> UIImage? {
        if let image = CGImageCreateWithImageInRect(self.CGImage, rect) {
            return UIImage(CGImage: image)
        } else if let image = (self.CIImage)?.imageByCroppingToRect(rect) {
            return UIImage(CIImage: image)
        }
       return nil
   }
}

1

Hier ist eine aktualisierte Swift 3-Version, die auf der Antwort von Noodles basiert

func cropping(to rect: CGRect) -> UIImage? {

    if let cgCrop = cgImage?.cropping(to: rect) {
        return UIImage(cgImage: cgCrop)
    }
    else if let ciCrop = ciImage?.cropping(to: rect) {
        return UIImage(ciImage: ciCrop)
    }

    return nil
}

1

Folgen Sie der Antwort von @Arne. Ich behebe mich nur auf die Kategoriefunktion. stelle es in die Kategorie von UIImage.

-(UIImage*)cropImage:(CGRect)rect{

    UIGraphicsBeginImageContextWithOptions(rect.size, false, [self scale]);
    [self drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
    UIImage* cropped_image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return cropped_image;
}

0

Ich war mit anderen Lösungen nicht zufrieden, weil sie entweder mehrmals zeichnen (mehr Strom verbrauchen als nötig) oder Probleme mit der Orientierung haben. Folgendes habe ich für ein skaliertes quadratisches CroppedImage aus einem UIImage * -Bild verwendet.

CGFloat minimumSide = fminf(image.size.width, image.size.height);
CGFloat finalSquareSize = 600.;

//create new drawing context for right size
CGRect rect = CGRectMake(0, 0, finalSquareSize, finalSquareSize);
CGFloat scalingRatio = 640.0/minimumSide;
UIGraphicsBeginImageContext(rect.size);

//draw
[image drawInRect:CGRectMake((minimumSide - photo.size.width)*scalingRatio/2., (minimumSide - photo.size.height)*scalingRatio/2., photo.size.width*scalingRatio, photo.size.height*scalingRatio)];

UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

0

Ich benutze die Methode unten.

  -(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect
  {
    CGSize cropSize = rect.size;
    CGFloat widthScale =  
    image.size.width/self.imageViewOriginal.bounds.size.width;
    CGFloat heightScale = 
    image.size.height/self.imageViewOriginal.bounds.size.height;
    cropSize = CGSizeMake(rect.size.width*widthScale,  
    rect.size.height*heightScale);
    CGPoint  pointCrop = CGPointMake(rect.origin.x*widthScale, 
    rect.origin.y*heightScale);
    rect = CGRectMake(pointCrop.x, pointCrop.y, cropSize.width, 
    cropSize.height);
    CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect);
    UIImage *croppedImage = [UIImage imageWithCGImage:subImage];
    CGImageRelease(subImage);
    return croppedImage;

}}


0

Schauen Sie sich https://github.com/vvbogdan/BVCropPhoto an

- (UIImage *) croppedImage {
    CGFloat scale = self.sourceImage.size.width / self.scrollView.contentSize.width;

    UIImage * finalImage = nil;
    CGRect targetFrame = CGRectMake ((self.scrollView.contentInset.left + self.scrollView.contentOffset.x) * scale,
            (self.scrollView.contentInset.top + self.scrollView.contentOffset.y) * scale,
            self.cropSize.width * scale,
            self.cropSize.height * scale);

    CGImageRef contextImage = CGImageCreateWithImageInRect ([[self imageWithRotation: self.sourceImage] CGImage], targetFrame);

    if (contextImage! = NULL) {
        finalImage = [UIImage imageWithCGImage: contextImage
                                         scale: self.sourceImage.scale
                                   Orientierung: UIImageOrientationUp];

        CGImageRelease (contextImage);
    }}

    return finalImage;
}}


- (UIImage *) imageWithRotation: (UIImage *) image {


    if (image.imageOrientation == UIImageOrientationUp) return image;
    CGAffineTransform transform = CGAffineTransformIdentity;

    switch (image.imageOrientation) {
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate (transform, image.size.width, image.size.height);
            transform = CGAffineTransformRotate (transform, M_PI);
            brechen;

        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            transform = CGAffineTransformTranslate (transform, image.size.width, 0);
            transform = CGAffineTransformRotate (transform, M_PI_2);
            brechen;

        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate (transform, 0, image.size.height);
            transform = CGAffineTransformRotate (transform, -M_PI_2);
            brechen;
        case UIImageOrientationUp:
        case UIImageOrientationUpMirrored:
            brechen;
    }}

    switch (image.imageOrientation) {
        case UIImageOrientationUpMirrored:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate (transform, image.size.width, 0);
            transform = CGAffineTransformScale (transform, -1, 1);
            brechen;

        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate (transform, image.size.height, 0);
            transform = CGAffineTransformScale (transform, -1, 1);
            brechen;
        case UIImageOrientationUp:
        case UIImageOrientationDown:
        case UIImageOrientationLeft:
        case UIImageOrientationRight:
            brechen;
    }}

    // Jetzt zeichnen wir das zugrunde liegende CGImage in einen neuen Kontext und wenden die Transformation an
    // oben berechnet.
    CGContextRef ctx = CGBitmapContextCreate (NULL, image.size.width, image.size.height,
            CGImageGetBitsPerComponent (image.CGImage), 0,
            CGImageGetColorSpace (image.CGImage),
            CGImageGetBitmapInfo (image.CGImage));
    CGContextConcatCTM (ctx, transform);
    switch (image.imageOrientation) {
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            // Grr ...
            CGContextDrawImage (ctx, CGRectMake (0, 0, image.size.height, image.size.width), image.CGImage);
            brechen;

        Standard:
            CGContextDrawImage (ctx, CGRectMake (0, 0, image.size.width, image.size.height), image.CGImage);
            brechen;
    }}

    // Und jetzt erstellen wir einfach ein neues UIImage aus dem Zeichnungskontext
    CGImageRef cgimg = CGBitmapContextCreateImage (ctx);
    UIImage * img = [UIImage imageWithCGImage: cgimg];
    CGContextRelease (ctx);
    CGImageRelease (cgimg);
    return img;

}}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.