Ritaglio di un UIImage


209

Ho del codice che ridimensiona un'immagine in modo da poter ottenere un pezzo ridimensionato del centro dell'immagine - lo uso per prendere un UIImagee restituire una piccola rappresentazione quadrata di un'immagine, simile a quello che si vede nella vista dell'album di l'app Foto. (So ​​che potrei usare a UIImageViewe regolare la modalità di ritaglio per ottenere gli stessi risultati, ma a volte queste immagini vengono visualizzate in UIWebViews).

Ho iniziato a notare alcuni arresti anomali in questo codice e sono un po 'perplesso. Ho due diverse teorie e mi chiedo se uno dei due sia sulla base.

Teoria 1) Ottengo il ritaglio disegnando in un contesto di immagine fuori schermo delle dimensioni del mio obiettivo. Poiché desidero la parte centrale dell'immagine, ho impostato l' CGRectargomento passato adrawInRect a qualcosa che è più grande dei limiti del contesto dell'immagine. Speravo che questo fosse Kosher, ma sto invece tentando di attingere ad altri ricordi che non dovrei toccare?

Teoria 2) Sto facendo tutto questo in un thread di background. So che ci sono parti di UIKit che sono limitate al thread principale. Stavo supponendo / sperando che passare a una vista fuori schermo non fosse uno di questi. Ho sbagliato?

(Oh, quanto mi manca il NSImage's drawInRect:fromRect:operation:fraction:metodo.)


Se stai provando a diagnosticare un arresto anomalo, dovresti eseguire l'app sotto il debugger e prendere nota di ciò che accade quando si verifica un arresto anomalo. Non hai nemmeno identificato se viene generata un'eccezione o ricevi EXC_BAD_ACCESS a causa di un puntatore penzolante. Una volta che lo sai, allora puoi iniziare a fare teorie.
benzado,

Giusto. Non ho riprogrammato questo con il debugger, anche se ho i messaggi EXC_BAD_ACCESS nel registro degli arresti anomali. Quando l'ho pubblicato, stavo lavorando supponendo che avessi fatto uno stupido errore nella mia implementazione e qualcuno ci avrebbe saltato sopra (come dimenticare un tracciato di ritaglio).
Jablair,

Per ciò che vale e nonostante le buone risposte di seguito, c'è un buon sondaggio sulle tecniche e le prestazioni relative su NSHipster: nshipster.com/image-resizing . Il purista in me voleva usare CIImage, ma il pragmatico ha scelto il contesto UIKit / immagine.
Chris Conover,

Risposte:


236

Aggiornamento 2014-05-28: ho scritto questo quando iOS 3 o giù di lì era la novità, sono sicuro che ci sono modi migliori per farlo ora, possibilmente integrato. Come molte persone hanno già detto, questo metodo non tiene conto della rotazione; leggi alcune risposte aggiuntive e diffondi un po 'di amore positivo per mantenere le risposte a questa domanda utili a tutti.

Risposta originale:

Ho intenzione di copiare / incollare la mia risposta alla stessa domanda altrove:

Non esiste un metodo di classe semplice per farlo, ma esiste una funzione che puoi usare per ottenere i risultati desiderati: CGImageCreateWithImageInRect(CGImageRef, CGRect)ti aiuterà.

Ecco un breve esempio che lo utilizza:

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

13
Quando imageOrientationè impostato su tutt'altro che in alto, CGImageviene ruotato rispetto al UIImagerettangolo di ritaglio e sarà errato. È possibile ruotare il rettangolo di ritaglio di conseguenza, ma il nuovo creato UIImageavrà imageOrientationsu, in modo che il ritaglio CGImagevenga ruotato al suo interno.
zoul,

25
Voglio sottolineare che sul display della retina è necessario raddoppiare la larghezza e l'altezza del cropRect usando questo metodo. Solo qualcosa in cui mi sono imbattuto.
petrocket,

21
per farlo funzionare con qualsiasi orientamento utilizzare:[UIImage imageWithCGImage:imageRef scale:largeImage.scale orientation:largeImage.imageOrientation];
Nicos Karalis

3
È necessario aggiungere il supporto per Retina !!
Mário Carvalho,

1
@NicosKaralis che lo ruoterà solo nel giusto orientamento. Tuttavia, l'area che stai ritagliando dall'immagine sarà comunque errata.
Tim Bodeit,

90

Per ritagliare le immagini della retina mantenendo la stessa scala e orientamento, utilizzare il seguente metodo in una categoria UIImage (iOS 4.0 e versioni successive):

- (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
Potresti semplicemente uccidere il condizionale qui e sempre multiplo self.scale, no?
Michael Mior,

2
Hai ragione, Michael, ma penso che la soluzione di cui sopra sia leggermente più veloce sui dispositivi non retina in quanto esegue solo un singolo controllo anziché quattro moltiplicazioni più un compito. Sui dispositivi retina è solo un singolo controllo booleano più del necessario, quindi è una questione di preferenze personali o adattamento del bersaglio.
CGee,

2
È corretto utilizzare CGImageRelease(imageRef);con ARC abilitato?
CGee,


3
In realtà 4 moltiplicazioni potrebbero essere più veloci della valutazione di una condizione :) Le moltiplicazioni possono essere ottimizzate piuttosto bene su CPU moderne mentre le condizioni possono essere solo previste. Ma hai ragione, è una questione di preferenza. Preferisco il codice più breve / più semplice a causa della maggiore leggibilità.
Marsbear

65

Puoi creare una categoria UIImage e usarla dove ti serve. Basato sulla risposta e sui commenti di HitScans di seguito.

@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

Puoi usarlo in questo modo:

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

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

UIImage *croppedImage = [imageToCrop crop:cropRect];

3
Non dovrebbe [[UIScreen mainScreen] scale]essere self.scale? La scala dell'immagine potrebbe non essere uguale alla scala dello schermo.
Michael Mior,

Ciao, ho provato la tua risposta e mi dà No visible @interface for 'UIImage' declares the selector 'crop'anche se ho inserito i file delle categorie .h e .m nel mio progetto e importato .h nella classe che sto usando. Qualche idea?
Ali,

Aggiustato. Ho perso l'inserimento del metodo nel file UIImage + Crop.h.
Ali,

Voglio ritagliare l'immagine in forma circolare. In modo che possiamo vedere solo il percorso circolare e l'altro percorso rimane trasparente.
Alfa

Quindi puoi usare CGImageCreateWithMask o CGImageCreateWithMaskingColors invece di CGImageCreateWithImageInRect
Vilém Kurz

54

Versione Swift 3

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

inserisci qui la descrizione dell'immagine

con l'aiuto di questa funzione bridge CGRectMake-> CGRect(crediti a questa risposta con risposta @rob mayoff):

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

L'utilizzo è:

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)
    )
}

Produzione:

inserisci qui la descrizione dell'immagine


Non sono sicuro del motivo per cui è stato votato, ho pensato che fosse una buona risposta. L'unica cosa che mi viene in mente è che non gestisce il ridimensionamento, quindi non funzionerà se si dispone di immagini @ 2x / @ 3x e i nomi delle funzioni non corrispondono.
William T.

4
Hai bisogno guarddella prima riga nel caso in cui UIImage.CGImageritorni nile perché usare varquando letfunziona perfettamente.
NoodleOfDeath il

Questo è corretto, TUTTAVIA, leggi cropping(to:)attentamente i documenti . Il risultato CGImagecontiene un forte riferimento al più grande CGImageda cui è stato ritagliato. Quindi, se vuoi solo aggrapparti all'immagine ritagliata e non hai bisogno dell'originale, dai un'occhiata alle altre soluzioni.
jsadler,

Questo non funziona con le immagini che hanno tag di orientamento. La regione di taglio non viene ruotata correttamente.
Max

1
@Max questo perché cgImage non ha il supporto dell'orientamento. Se UIImage sta avendo rightallora il cgImage dovrebbe essere ruotato in senso antiorario di 90 gradi, quindi anche il rettangolo di ritaglio dovrebbe essere ruotato
MK Yung

49

Ecco la mia implementazione del raccolto di UIImage che obbedisce alla proprietà imageOrientation. Tutti gli orientamenti sono stati accuratamente testati.

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
L'avvertenza implicit declaration of function 'rad' is invalid in c99può essere rimossa sostituendo: rad (90), rad (-90), rad (-180) -> M_PI_2, -M_PI_2, -_M_PI
Sound Blaster

Oops scusa. Aggiunta la funzione di utilità rad () allo snippet di origine.
Sergii Rudchenko,

contains undefined reference for architecture armv7Mi sto perdendo una biblioteca? CoreGraphics è importato.
Bob Spryn,

1
@SergiiRudchenko "Tutti gli orientamenti sono stati accuratamente testati." - Questo include gli orientamenti speculari?
Tim Bodeit,

@BobSpryn No, non ti manca una libreria. Anche se non riesco a spiegare cosa significhi l'errore, la sostituzione di rad (), come suggerito da SoundBlaster, corregge anche questo errore.
Tim Bodeit,

39

Heads up: tutte queste risposte assumono un CGImageoggetto immagine con il dorso.

image.CGImagepuò restituire zero, se il valore UIImageè supportato da a CIImage, che sarebbe il caso se si creasse questa immagine usando a CIFilter.

In tal caso, potrebbe essere necessario disegnare l'immagine in un nuovo contesto e restituire quell'immagine ( lenta ).

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
Questo è esattamente ciò di cui avevo bisogno, funziona in tutti gli scenari quindi meglio di qualsiasi altra soluzione!
David Thompson,

2
image.size nella prima riga dovrebbe essere rect.size UIGraphicsBeginImageContextWithOptions (rect.size, false, image.scale);
Brett,

2
Grazie Brett, sicuramente proprio lì. Ho aggiornato il codice per includere questa correzione.
colinta,

Perché -rect.origin.x non è solo rect.origin.x?
Aggressor,

2
Perché stiamo ritagliando; in altre parole, l'arg "rect" sta dicendo "Voglio che la parte superiore sinistra della nuova immagine inizi, diciamo, [10, 10]". Per fare ciò, disegniamo l'immagine in modo che [10, 10] sia all'origine della nuova immagine.
colinta,

35

Nessuna delle risposte qui gestisce correttamente tutti i problemi di scala e rotazione al 100%. Ecco una sintesi di tutto ciò che è stato detto finora, aggiornato a partire da iOS7 / 8. È pensato per essere incluso come metodo in una categoria su UIImage.

- (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
Perché questa risposta non viene più votata? Ho provato tutte le risposte di cui sopra e mi sono imbattuto in tonnellate di problemi (in particolare la conversione da UIImage a CIImage e la perdita di dati di trasformazione adeguati). QUESTO È L'UNICO CHE FUNZIONA PER ME!
Aggressor,

1
Grazie @Aggressor, felice che questo funzioni per te. La mia risposta è stata pubblicata solo un mese fa, mentre molte di queste risposte sono qui da 5 anni. Immagino che ciò spieghi la mancanza di voti comparabili.
lupo,

Sto usando questo per scorrere i filtri tra le immagini (come fa Snapchat) e sto riscontrando lievi problemi di ridimensionamento. C'è qualcosa nel tuo codice che puoi suggerire di guardare che potrebbe contenere la causa di questo errore di ridimensionamento? Ho pubblicato la mia soluzione che utilizza il tuo codice qui: stackoverflow.com/questions/23319497/…
Aggressor

una funzione molto utile, grazie. qualche consiglio che cosa dovrebbe essere modificato per ritagliare concentrandosi sulla parte centrale dell'immagine?
privato

QUESTA DOVREBBE ESSERE LA RISPOSTA.
ferro di

17

Versione rapida della awolfrisposta, che ha funzionato per me:

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);

La maggior parte delle risposte che ho visto riguarda solo una posizione di (0, 0) per (x, y). Ok, questo è un caso, ma vorrei che la mia operazione di ritaglio fosse centrata. Quello che mi ci è voluto un po 'per capire è la linea che segue il commento del WTF.

Prendiamo il caso di un'immagine catturata con un orientamento verticale:

  1. L'altezza dell'immagine originale è superiore alla sua larghezza (Woo, finora nessuna sorpresa!)
  2. L'immagine che il metodo CGImageCreateWithImageInRect immagina nel suo mondo non è in realtà un ritratto ma un paesaggio (Questo è anche il motivo per cui se non usi l'argomento di orientamento nel costruttore imageWithCGImage, apparirà come ruotato di 180).
  3. Quindi, dovresti immaginare che sia un paesaggio, la posizione (0, 0) è l'angolo in alto a destra dell'immagine.

Spero abbia senso! In caso contrario, prova diversi valori e vedrai che la logica è invertita quando si tratta di scegliere la giusta x, y, larghezza e altezza per il tuo cropRect.


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

Estensione rapida

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

La migliore soluzione per ritagliare un UIImage in Swift , in termini di precisione, ridimensionamento dei pixel ...:

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
}

Istruzioni utilizzate per chiamare questa funzione:

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

Nota: l'ispirazione iniziale del codice sorgente scritta in Objective-C è stata trovata sul blog "Cocoanetics".


3

Lo snippet di codice seguente potrebbe essere di aiuto.

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

Sembra un po 'strano ma funziona benissimo e prende in considerazione l'orientamento dell'immagine:

var image:UIImage = ...

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

1
ma ovviamente rompe la scala
Sulthan il

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

Su iOS9.2SDK, utilizzo il metodo seguente per convertire i frame da UIView a UIImage

-(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

Aggiornamento Swift 2.0 ( CIImagecompatibilità)

Espandendosi dalla risposta di Maxim, ma funziona anche se l'immagine è CIImagebasata.

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

Ecco una versione aggiornata di Swift 3 basata sulla risposta di Noodles

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

Segui la risposta di @Arne. Ho appena risolto la funzione Categoria. metterlo nella categoria di 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

Non ero soddisfatto di altre soluzioni perché o disegnavano più volte (usando più potenza del necessario) o hanno problemi di orientamento. Ecco cosa ho usato per un quadrato ritagliato ridimensionatoImmagine da un'immagine UIImage *.

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

Uso il metodo seguente.

  -(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

Guarda https://github.com/vvbogdan/BVCropPhoto

- (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) * scala,
            (self.scrollView.contentInset.top + self.scrollView.contentOffset.y) * scala,
            scala self.cropSize.width *,
            self.cropSize.height * scale);

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

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

        CGImageRelease (contextImage);
    }

    return finalImage;
}


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


    if (image.imageOrientation == UIImageOrientationUp) restituisce l'immagine;
    CGAffineTransform transform = CGAffineTransformIdentity;

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

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

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

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

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

    // Ora disegniamo il CGImage sottostante in un nuovo contesto, applicando la trasformazione
    // calcolato sopra.
    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);
            rompere;

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

    // E ora creiamo solo un nuovo UIImage dal contesto del disegno
    CGImageRef cgimg = CGBitmapContextCreateImage (ctx);
    UIImage * img = [UIImage imageWithCGImage: cgimg];
    CGContextRelease (CTX);
    CGImageRelease (cgimg);
    ritorno img;

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.