Cortando uma UIImage

209

Eu tenho um código que redimensiona uma imagem para que eu possa obter uma parte do centro da imagem em escala - eu uso isso para tirar UIImagee retornar uma representação quadrada pequena de uma imagem, semelhante ao que é visto na exibição do álbum de uma imagem. o aplicativo Fotos. (Eu sei que poderia usar UIImageViewae ajustar o modo de corte para obter os mesmos resultados, mas essas imagens às vezes são exibidas em UIWebViews).

Comecei a perceber algumas falhas neste código e estou um pouco perplexo. Eu tenho duas teorias diferentes e estou me perguntando se alguma delas está na base.

Teoria 1) Eu obtenho o corte desenhando em um contexto de imagem fora da tela do meu tamanho-alvo. Como quero a parte central da imagem, defino o CGRectargumento passado drawInRectpara algo maior que os limites do contexto da minha imagem. Eu esperava que isso fosse Kosher, mas estou tentando atrair outras memórias que não deveria estar tocando?

Teoria 2) Estou fazendo tudo isso em um thread de segundo plano. Eu sei que existem partes do UIKit restritas ao segmento principal. Eu estava assumindo / esperando que desenhar para uma visualização fora da tela não fosse um deles. Estou errado?

(Oh, como eu sinto falta do NSImage's drawInRect:fromRect:operation:fraction:método.)

Jablair
fonte
Se você estiver tentando diagnosticar uma falha, execute o aplicativo no depurador e tome nota do que acontece quando ela falha. Você nem sequer identificou se há uma exceção sendo lançada ou está recebendo EXC_BAD_ACCESS devido a um ponteiro pendente. Depois que você souber disso, poderá começar a fazer teorias.
benzado
Justo. Eu não reproduzi isso no depurador, embora eu tenha mensagens EXC_BAD_ACCESS no log de falha. Quando publiquei isso, estava trabalhando com a suposição de que eu cometi um erro estúpido em minha implementação e alguém pularia nela (como esquecer um traçado de recorte).
Jablair #
Apesar do que vale a pena e de boas respostas abaixo, há uma boa pesquisa de técnicas e desempenho no NSHipster: nshipster.com/image-resizing . O purista em mim queria usar o CIImage, mas o pragmatista escolheu o contexto UIKit / image.
Chris Conover

Respostas:

236

Atualização 28-05-2014: escrevi isso quando o iOS 3 era a novidade, estou certo de que existem maneiras melhores de fazer isso agora, possivelmente embutidas. Como muitas pessoas mencionaram, esse método não leva em consideração a rotação; leia algumas respostas adicionais e espalhe um pouco de amor positivo para manter as respostas a essa pergunta úteis para todos.

Resposta original:

Vou copiar / colar minha resposta para a mesma pergunta em outro lugar:

Não existe um método simples de classe para fazer isso, mas existe uma função que você pode usar para obter os resultados desejados: CGImageCreateWithImageInRect(CGImageRef, CGRect)irá ajudá-lo.

Aqui está um pequeno exemplo de como usá-lo:

CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect);
// or use the UIImage wherever you like
[UIImageView setImage:[UIImage imageWithCGImage:imageRef]]; 
CGImageRelease(imageRef);
HitScan
fonte
13
Quando o valor imageOrientationestá definido para qualquer coisa, exceto para cima, o valor CGImageé girado em relação ao UIImageretângulo de recorte e o erro fica errado. Você pode girar o retângulo de corte de acordo, mas o recém-criado UIImageserá imageOrientationexibido, para que o corte CGImageseja girado dentro dele.
zoul
25
Quero ressaltar que, na tela da retina, você precisa dobrar a largura e a altura do cropRect usando esse método. Apenas algo que eu encontrei.
petrocket
21
Para torná-lo funciona em qualquer uso orientação:[UIImage imageWithCGImage:imageRef scale:largeImage.scale orientation:largeImage.imageOrientation];
Nicos Karalis
3
Você deve adicionar suporte para o Retina !!
Mário Carvalho
1
@NicosKaralis que apenas o girará na orientação correta. No entanto, a área que você está cortando da imagem ainda estará errada.
precisa saber é o seguinte
90

Para cortar imagens da retina, mantendo a mesma escala e orientação, use o seguinte método em uma categoria UIImage (iOS 4.0 e posterior):

- (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;
}
Arne
fonte
14
Você poderia simplesmente matar o condicional aqui e sempre múltiplo por self.scale, não?
Michael Mior
2
Você está certo, Michael, mas acho que a solução acima é um pouco mais rápida em dispositivos que não são da retina, pois apenas faz uma verificação única em vez de quatro multiplicações e uma atribuição. Nos dispositivos de retina, é apenas uma única verificação booleana mais do que o necessário, por isso é uma questão de preferência pessoal ou ajuste ao alvo.
CGEE
2
É correto usar CGImageRelease(imageRef);com o ARC ativado?
CGEE
3
@Dschee Sim. Veja stackoverflow.com/questions/7800174/…
Klaas
3
Na verdade, 4 multiplicações podem ser mais rápidas do que avaliar uma condição :) As multiplicações podem ser otimizadas muito bem em CPUs modernas, enquanto as condições só podem ser previstas. Mas você está certo, é uma questão de preferência. Eu prefiro o código mais curto / mais simples devido ao aumento da legibilidade.
Marsbear
65

Você pode criar uma categoria UIImage e usá-la sempre que precisar. Baseado na resposta e nos comentários do HitScans abaixo.

@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

Você pode usá-lo desta maneira:

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

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

UIImage *croppedImage = [imageToCrop crop:cropRect];
Vilém Kurz
fonte
3
Não deveria [[UIScreen mainScreen] scale]ser apenas self.scale? A escala da imagem pode não ser igual à escala da tela.
Michael Mior
Oi, eu tentei sua resposta e ela me fornece, No visible @interface for 'UIImage' declares the selector 'crop'mesmo que eu coloque os arquivos de categoria .h e .m no meu projeto e importei o .h na classe que estou usando a categoria. Qualquer ideia?
Ali
Corrigido. Eu senti falta de colocar o cabeçalho do método no arquivo UIImage + Crop.h.
Ali
Quero cortar a imagem em forma circular. Para que possamos ver apenas o caminho circular e outro caminho permanece transparente.
Alfa
Do que você pode usar CGImageCreateWithMask ou CGImageCreateWithMaskingColors vez de CGImageCreateWithImageInRect
Vilém Kurz
54

Versão 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

insira a descrição da imagem aqui

com a ajuda desta função de ponte CGRectMake-> CGRect(créditos para esta resposta respondida por @rob mayoff):

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

O uso é:

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

Resultado:

insira a descrição da imagem aqui

Maxim Shoustin
fonte
Não tendo certeza do motivo pelo qual foi recusado, achei que era uma boa resposta. A única coisa em que consigo pensar é que ele não lida com o dimensionamento, portanto não funcionará se você tiver imagens @ 2x / @ 3x e os nomes das suas funções não coincidirem.
William T.
4
Você precisa guardda primeira linha no caso de UIImage.CGImageretornos nile por que usar varquando letfunciona perfeitamente.
NoodleOfDeath
Está correto, contudo, leia os documentos com cropping(to:)muito cuidado. O resultado CGImagemantém uma forte referência ao maior CGImagedo qual foi cortado. Portanto, se você quiser se apegar apenas à imagem cortada e não precisar do original, dê uma olhada nas outras soluções.
jsadler
Isso não funciona com imagens que possuem tags de orientação. A região de corte não é rotacionada corretamente.
Max
1
@ Isso ocorre porque o cgImage não possui suporte para orientação. Se UIImage está a ter right, em seguida, o CGImage deve ser ccw uma rotação de 90 graus, por conseguinte, a rect colheita deve ser rodada demais
MK Yung
49

Aqui está minha implementação de corte UIImage que obedece à propriedade imageOrientation. Todas as orientações foram exaustivamente testadas.

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;
}
Sergii Rudchenko
fonte
7
O aviso implicit declaration of function 'rad' is invalid in c99pode ser removido por substituição: rad (90), rad (-90), rad (-180) -> M_PI_2, -M_PI_2, -_M_PI
Sound Blaster
OPA, desculpe. Adicionada a função de utilidade rad () ao snippet de origem.
Sergii Rudchenko
contains undefined reference for architecture armv7Estou faltando uma biblioteca? CoreGraphics é importado.
Bob Spryn
1
@SergiiRudchenko "Todas as orientações foram exaustivamente testadas." - Isso inclui as orientações espelhadas?
precisa saber é o seguinte
@BobSpryn Não, você não está perdendo uma biblioteca. Embora eu não possa explicar o que significa o erro, a substituição de rad (), como sugeriu o SoundBlaster, também corrige esse erro.
precisa saber é o seguinte
39

CGImageAtenção : todas essas respostas assumem um objeto de imagem com backup.

image.CGImagepode retornar nulo, se UIImagefor apoiado por a CIImage, o que seria o caso se você criasse esta imagem usando a CIFilter.

Nesse caso, talvez você precise desenhar a imagem em um novo contexto e retornar a imagem ( 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;
}
colinta
fonte
5
Isso é exatamente o que eu precisava, funciona em todos os cenários, portanto, melhor do que qualquer outra solução!
David Thompson
2
image.size na primeira linha deve ser rect.size UIGraphicsBeginImageContextWithOptions (rect.size, false, image.scale);
Brett
2
Obrigado Brett, definitivamente ali. Atualizei o código para incluir essa correção.
colinta
Por que -rect.origin.x não é apenas rect.origin.x?
Aggressor
2
Porque estamos cortando; em outras palavras, o argumento 'rect' está dizendo "Quero que o canto superior esquerdo da nova imagem comece em, digamos, [10, 10]". Para fazer isso, desenhamos a imagem para que [10, 10] esteja na origem da nova imagem.
colinta 23/09
35

Nenhuma das respostas aqui lida com todos os problemas de escala e rotação 100% corretamente. Aqui está uma síntese de tudo o que foi dito até agora, atualizado a partir do iOS7 / 8. Ele deve ser incluído como método em uma categoria no 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;
}
um lobo
fonte
1
Por que essa resposta não é mais votada? Tentei todas as respostas acima e tive vários problemas (principalmente a conversão de UIImage para CIImage e a perda de dados de transformação adequados). ESTE É O ÚNICO QUE FUNCIONA PARA MIM!
Aggressor
1
Obrigado @Aggressor, feliz que isso funcione para você. Minha resposta foi postada apenas um mês atrás, enquanto muitas dessas respostas estão aqui há 5 anos. Acho que isso explica a falta de votos comparáveis.
awolf 23/09
Estou usando isso para deslizar os filtros pelas imagens (como o snapchat) e estou tendo alguns problemas de dimensionamento. Existe alguma coisa no seu código que você possa sugerir, que possa conter a causa desse erro de dimensionamento? Postei minha solução que usa seu código aqui: stackoverflow.com/questions/23319497/…
Aggressor
uma função muito útil, obrigado. alguma dica o que deve ser modificado para recortar o foco na parte central da imagem?
private
ESTA DEVE SER A RESPOSTA.
horseshoe7
17

Versão rápida da awolfresposta de, que funcionou para mim:

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
    }
}
Mark Leonard
fonte
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);

A maioria das respostas que eu vi trata apenas de uma posição de (0, 0) para (x, y). Ok, esse é um caso, mas eu gostaria que minha operação de corte estivesse centralizada. O que demorei um pouco para descobrir é a linha que segue o comentário da WTF.

Vamos considerar o caso de uma imagem capturada com uma orientação retrato:

  1. A altura da imagem original é maior que sua largura (Woo, nenhuma surpresa até agora!)
  2. A imagem que o método CGImageCreateWithImageInRect imagina em seu próprio mundo não é realmente um retrato, mas uma paisagem (também é por isso que se você não usar o argumento de orientação no construtor imageWithCGImage, ele será exibido como 180 rotacionado).
  3. Então, você deve imaginar que é uma paisagem, a posição (0, 0) sendo o canto superior direito da imagem.

Espero que faça sentido! Caso contrário, tente valores diferentes. Você verá que a lógica é invertida quando se trata de escolher x, y, largura e altura corretas para o seu cropRect.

Jordânia
fonte
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)
    }
}
neoneye
fonte
7

Extensão rápida

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
    }
}
Byte épico
fonte
4

Melhor solução para cortar uma UIImage no Swift , em termos de precisão, escala de pixels ...:

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
}

Instruções usadas para chamar esta função:

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

Nota: a inspiração inicial do código fonte escrita em Objective-C foi encontrada no blog "Cocoanetics".

O rei da bruxaria
fonte
3

Abaixo o snippet de código pode ajudar.

import UIKit

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

Parece um pouco estranho, mas funciona muito bem e leva em consideração a orientação da imagem:

var image:UIImage = ...

let img = CIImage(image: image)!.imageByCroppingToRect(rect)
image = UIImage(CIImage: img, scale: 1, orientation: image.imageOrientation)
MuniekMg
fonte
1
mas, obviamente, quebra a escala
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
    }
}
huync
fonte
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;
}
Dourado
fonte
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;
}
Bhushan_pawar
fonte
1

No iOS9.2SDK, uso o método abaixo para converter quadros de UIView em 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;
}
user5176302
fonte
1

Atualização Swift 2.0 ( CIImagecompatibilidade)

Expansão da resposta de Maxim, mas funciona se sua imagem também for CIImagebaseada.

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
   }
}
NoodleOfDeath
fonte
1

Aqui está uma versão atualizada do Swift 3 com base na resposta 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
}
voidref
fonte
1

Siga a resposta de @Arne. Eu apenas corrigindo a função Categoria. coloque-o na categoria 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;
}
Linh Nguyen
fonte
0

Não fiquei satisfeito com outras soluções, porque elas demoram várias vezes (usando mais energia do que o necessário) ou têm problemas de orientação. Aqui está o que eu usei para um quadrado croppedImage em escala de uma imagem 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();
Matthieu Rouif
fonte
0

Eu uso o método abaixo.

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

}

user5176302
fonte
0

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

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

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

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

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

        CGImageRelease (contextImage);
    }

    return finalImage;
}


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


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

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

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

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

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

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

    // Agora, desenhamos o CGImage subjacente em um novo contexto, aplicando a transformação
    // calculado acima.
    CGContextRef ctx = CGBitmapContextCreate (NULL, image.size.width, image.size.height,
            CGImageGetBitsPerComponent (image.CGImage), 0,
            CGImageGetColorSpace (image.CGImage),
            CGImageGetBitmapInfo (image.CGImage));
    CGContextConcatCTM (ctx, transformação);
    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);
            pausa;

        padrão:
            CGContextDrawImage (ctx, CGRectMake (0, 0, image.size.width, image.size.height), image.CGImage);
            pausa;
    }

    // E agora criamos uma nova UIImage a partir do contexto do desenho
    CGImageRef cgimg = CGBitmapContextCreateImage (ctx);
    UIImage * img = [UIImage imageWithCGImage: cgimg];
    CGContextRelease (ctx);
    CGImageRelease (cgimg);
    return img;

}
Nowytech
fonte