A maneira mais simples de redimensionar uma UIImage?


397

No meu aplicativo para iPhone, tiro uma foto com a câmera e desejo redimensioná-la para 290 * 390 pixels. Eu estava usando esse método para redimensionar a imagem:

UIImage *newImage = [image _imageScaledToSize:CGSizeMake(290, 390)
                         interpolationQuality:1];    

Funciona perfeitamente, mas é uma função não documentada, por isso não posso mais usá-la no iPhone OS4.

Então ... qual é a maneira mais simples de redimensionar uma UIImage?


A maneira de fazê-lo em 2019, nshipster.com/image-resizing
dengST30

Respostas:


772

A maneira mais simples é definir o quadro UIImageViewe definir contentModeuma das opções de redimensionamento.

Ou você pode usar este método utilitário, se você realmente precisar redimensionar uma imagem:

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
    //UIGraphicsBeginImageContext(newSize);
    // In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
    // Pass 1.0 to force exact pixel size.
    UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
    [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
    UIGraphicsEndImageContext();
    return newImage;
}

Exemplo de uso:

#import "MYUtil.h"

UIImage *myIcon = [MYUtil imageWithImage:myUIImageInstance scaledToSize:CGSizeMake(20, 20)];

11
Eu não reteria o resultado aqui. newImagejá será liberado automaticamente e deve ser o método que chamou isso para mantê-lo ou não. Essa abordagem está apenas implorando por um erro de memória, pois initnão faz parte do nome do método. Eu esperaria que um método nomeado como esse retornasse um objeto liberado automaticamente.
Alex Wayne

Ponto válido e editado. Vou deixar o aviso de que meu código que usa isso gera erros malloc em outro lugar sem uma retenção adicional, o que é claramente minha culpa de alguma forma :-).
Paul Lynch

26
No iOS 4.0 , as funções do UIGraphics * são todas seguras para threads.
BJ Homer

3
@ Paul Lynch: Lembre-se de que essa alteração quebra a missão da postagem original: como redimensionar uma imagem para um tamanho exato de pixel (em oposição ao tamanho do ponto ).
Nikolai Ruhe

8
Para aqueles que lutam devido à @NikolaiRuhe limitação apontada, você pode forçá-lo a um tamanho exato de pixels, passando de 1,0 em vez de 0,0 na chamada paraUIGraphicsBeginImageContextWithOptions
Danny

84

Aqui está uma versão rápida da resposta de Paul Lynch

func imageWithImage(image:UIImage, scaledToSize newSize:CGSize) -> UIImage{
    UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0);
    image.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height))
    let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage
}

E como uma extensão:

public extension UIImage {
    func copy(newSize: CGSize, retina: Bool = true) -> UIImage? {
        // In next line, pass 0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
        // Pass 1 to force exact pixel size.
        UIGraphicsBeginImageContextWithOptions(
            /* size: */ newSize,
            /* opaque: */ false,
            /* scale: */ retina ? 0 : 1
        )
        defer { UIGraphicsEndImageContext() }

        self.draw(in: CGRect(origin: .zero, size: newSize))
        return UIGraphicsGetImageFromCurrentImageContext()
    }
}

2
apenas observou que, no iOS, sua vontade múltipla newSize por 2x, 3x com base no dispositivo
Sruit A.Suk

6
UIGraphicsBeginImageContextWithOptions (newSize, false, image.scale); para todos os dispositivos
phnmnn

Observe que isso não redimensiona a CGImage subjacente (pelo menos no iOS 12.1). O que é aceitável com base na pergunta, mas se você estiver escrevendo a imagem, precisará usar o cgImage e obterá a imagem original.
Prewett

72

Solução adequada do Swift 3.0 para iOS 10+ : uso ImageRenderere sintaxe de fechamento:

func imageWith(newSize: CGSize) -> UIImage {
    let renderer = UIGraphicsImageRenderer(size: newSize)
    let image = renderer.image { _ in
        self.draw(in: CGRect.init(origin: CGPoint.zero, size: newSize))
    }

    return image.withRenderingMode(self.renderingMode)
}

E aqui está a versão do Objective-C:

@implementation UIImage (ResizeCategory)
- (UIImage *)imageWithSize:(CGSize)newSize
{
    UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:newSize];
    UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext*_Nonnull myContext) {
        [self drawInRect:(CGRect) {.origin = CGPointZero, .size = newSize}];
    }];
    return [image imageWithRenderingMode:self.renderingMode];
}
@end

11
Solução muito elegante, esta deve ser a resposta aceita!
appsunited

2
Isso é melhor do que elegante, está correto. Os documentos da API da Apple aconselham o novo código a usar o UIGraphicsRenderer como esse código. Obrigado
Paul Bruneau

11
@appsunited A questão tem quase 8 anos. E esse método funciona apenas para iOS10 +, conforme indicado.

Ao copiar o novo objeto de imagem para a área de transferência, ele produz um bloco branco para mim. Na verdade, não está redimensionando a visualização da imagem?
Oliver Dixon

11
esta função retornando uma imagem de 0 altura e 0 largura, espera-se que a altura seja 9000 e 9000 de largura.
Krishan kumar 16/05/19

31

Trevor Howard tem algumas categorias UIImage que lidam com redimensionamento bastante bem. Se nada mais, você pode usar o código como exemplos.

Nota: No iOS 5.1, esta resposta pode ser inválida. Veja o comentário abaixo.


2
Eu também precisava redimensionar algumas imagens e tentei as adições de Trevor à UIImage, mas obtive alguns bugs estranhos nos PNGs (algo sobre o canal alfa). A resposta aceita para esta pergunta funcionou muito bem.
Sorig 02/11/10

11
Isto já não é válido (pelo menos com iOS5.1
Jann

@Jann, veja os commits abaixo do post.
vikingosegundo

@Jann Se você seguir as correções sugeridas nos comentários, vá com "Matt diz: 22 de novembro de 2011 às 17:39", porque ele libera o colorspaceref (ao contrário da solução semelhante da horseshoe7).
Ben Flynn

11
@sanmai usando qualquer um desses métodos de redimensionamento das imagens perdendo sua nitidez. Existe alguma maneira de alcançar a nitidez
vamsi575kg

31

Uma versão mais compacta para Swift 4 e iOS 10+:

extension UIImage {
    func resized(to size: CGSize) -> UIImage {
        return UIGraphicsImageRenderer(size: size).image { _ in
            draw(in: CGRect(origin: .zero, size: size))
        }
    }
}

Uso:

let resizedImage = image.resized(to: CGSize(width: 50, height: 50))

26

Também vi isso feito (que eu uso nos UIButtonsestados Normal e Selecionado, pois os botões não resizese ajustam). O crédito vai para quem foi o autor original.

Primeiro faça um arquivo .h e .m vazia chamada UIImageResizing.heUIImageResizing.m

// Put this in UIImageResizing.h
@interface UIImage (Resize)
- (UIImage*)scaleToSize:(CGSize)size;
@end

// Put this in UIImageResizing.m
@implementation UIImage (Resize)

- (UIImage*)scaleToSize:(CGSize)size {
UIGraphicsBeginImageContext(size);

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0.0, size.height);
CGContextScaleCTM(context, 1.0, -1.0);

CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, size.width, size.height), self.CGImage);

UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return scaledImage;
}

@end

Inclua esse arquivo .h em qualquer arquivo .m no qual você usará a função e chame-a assim:

UIImage* image = [UIImage imageNamed:@"largeImage.png"];
UIImage* smallImage = [image scaleToSize:CGSizeMake(100.0f,100.0f)];

Isso não copia sobre a orientação da imagem.
Kevin

20

Essa melhoria no código de Paul fornecerá uma imagem nítida em alta resolução em um iPhone com uma tela retina. Caso contrário, ao reduzir, fica embaçado.

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
    if ([[UIScreen mainScreen] scale] == 2.0) {
        UIGraphicsBeginImageContextWithOptions(newSize, YES, 2.0);
    } else {
        UIGraphicsBeginImageContext(newSize);
    }
} else {
    UIGraphicsBeginImageContext(newSize);
}
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
UIGraphicsEndImageContext();
return newImage;
}

13
Apenas para sua informação, as alterações que você fez são desnecessárias, pois fornecer um valor de 0,0 para a escala UIGraphicsBeginImageContextWithOptionsusará automaticamente a escala da tela principal (a partir do iOS 3.2).
Andrew R.

16

Aqui está uma maneira simples:

    UIImage * image = [UIImage imageNamed:@"image"];
    CGSize sacleSize = CGSizeMake(10, 10);
    UIGraphicsBeginImageContextWithOptions(sacleSize, NO, 0.0);
    [image drawInRect:CGRectMake(0, 0, sacleSize.width, sacleSize.height)];
    UIImage * resizedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

resizedImage é uma nova imagem.


16

Solução rápida para Stretch Fill, Aspect Fill e Aspect Fit

extension UIImage {
    enum ContentMode {
        case contentFill
        case contentAspectFill
        case contentAspectFit
    }

    func resize(withSize size: CGSize, contentMode: ContentMode = .contentAspectFill) -> UIImage? {
        let aspectWidth = size.width / self.size.width
        let aspectHeight = size.height / self.size.height

        switch contentMode {
        case .contentFill:
            return resize(withSize: size)
        case .contentAspectFit:
            let aspectRatio = min(aspectWidth, aspectHeight)
            return resize(withSize: CGSize(width: self.size.width * aspectRatio, height: self.size.height * aspectRatio))
        case .contentAspectFill:
            let aspectRatio = max(aspectWidth, aspectHeight)
            return resize(withSize: CGSize(width: self.size.width * aspectRatio, height: self.size.height * aspectRatio))
        }
    }

    private func resize(withSize size: CGSize) -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(size, false, self.scale)
        defer { UIGraphicsEndImageContext() }
        draw(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
        return UIGraphicsGetImageFromCurrentImageContext()
    }
}

e para usar, você pode fazer o seguinte:

let image = UIImage(named: "image.png")!
let newImage = image.resize(withSize: CGSize(width: 200, height: 150), contentMode: .contentAspectFill)

Agradecimentos a abdullahselek por sua solução original.


15

Aqui está uma modificação da categoria escrita por iWasRobbed acima. Ele mantém a proporção da imagem original em vez de distorcê-la.

- (UIImage*)scaleToSizeKeepAspect:(CGSize)size {
    UIGraphicsBeginImageContext(size);

    CGFloat ws = size.width/self.size.width;
    CGFloat hs = size.height/self.size.height;

    if (ws > hs) {
        ws = hs/ws;
        hs = 1.0;
    } else {
        hs = ws/hs;
        ws = 1.0;
    }

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(context, 0.0, size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    CGContextDrawImage(context, CGRectMake(size.width/2-(size.width*ws)/2,
        size.height/2-(size.height*hs)/2, size.width*ws,
        size.height*hs), self.CGImage);

    UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return scaledImage;
}

11

Se você deseja apenas uma imagem menor e não se importa com o tamanho exato:

+ (UIImage *)imageWithImage:(UIImage *)image scaledToScale:(CGFloat)scale
{
    UIGraphicsBeginImageContextWithOptions(self.size, YES, scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
    [self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

Definir escala para 0.25f fornecer uma imagem de 816 por 612 a partir de uma câmera de 8MP.

Aqui está uma categoria UIImage + Scale para quem precisa de um.


10

Por que tão complicado? Eu acho que o uso da API do sistema pode alcançar o mesmo resultado:

UIImage *largeImage;
CGFloat ratio = 0.4; // you want to get a new image that is 40% the size of large image.
UIImage *newImage = [UIImage imageWithCGImage:largeImage.CGImage
                                        scale:1/ratio
                                  orientation:largeImage.imageOrientation];
// notice the second argument, it is 1/ratio, not ratio.

O único problema é que você deve passar a razão inversa da meta como segundo argumento, pois, de acordo com o documento, o segundo parâmetro especifica a proporção da imagem original em comparação com a nova escala.


Porque escala (%) não é a mesma coisa que pixels (p / h).

A escala deve ser largeImage.scale / ratio e não 1 / ratio.
Marián Černý

9

Esta é uma extensão UIImage compatível com o Swift 3 e o Swift 4 que dimensiona a imagem para um determinado tamanho com uma proporção

extension UIImage {

    func scaledImage(withSize size: CGSize) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
        defer { UIGraphicsEndImageContext() }
        draw(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
        return UIGraphicsGetImageFromCurrentImageContext()!
    }

    func scaleImageToFitSize(size: CGSize) -> UIImage {
        let aspect = self.size.width / self.size.height
        if size.width / aspect <= size.height {
            return scaledImage(withSize: CGSize(width: size.width, height: size.width / aspect))
        } else {
            return scaledImage(withSize: CGSize(width: size.height * aspect, height: size.height))
        }
    }

}

Exemplo de uso

let image = UIImage(named: "apple")
let scaledImage = image.scaleImageToFitSize(size: CGSize(width: 45.0, height: 45.0))

Eu recomendaria nomear a função scaledImage(with size:)ou scaledWithSize(_:). Também UIGraphicsGetImageFromCurrentImageContextnão pode realmente retornar nil, portanto, !também funcionaria. Você também pode simplificar a criação correta para CGRect(origin: .zero, size: size).
Sulthan


6
 func resizeImage(image: UIImage, newWidth: CGFloat) -> UIImage 
{
        let scale = newWidth / image.size.width
        let newHeight = image.size.height * scale
        UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight))
        image.drawInRect(CGRectMake(0, 0, newWidth, newHeight))
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage
}

É uma boa ideia adicionar um texto descritivo para acompanhar seu código.
19596 GMchris

6

Para meus colegas xamarianos, aqui está uma versão Xamarin.iOS C # da resposta de @Paul Lynch.

private UIImage ResizeImage(UIImage image, CGSize newSize) 
{
    UIGraphics.BeginImageContextWithOptions(newSize, false, 0.0f);
    image.Draw(new CGRect(0, 0, newSize.Width, newSize.Height));
    UIImage newImage = UIGraphics.GetImageFromCurrentImageContext();
    UIGraphics.EndImageContext();
    return newImage;
}

5

Se você deseja criar uma miniatura de uma UIImage (com redimensionamento proporcional ou talvez algum corte envolvido), consulte a categoria UIImage + Resize que permite usar a sintaxe concisa semelhante ao ImageMagick:

UIImage* squareImage       = [image resizedImageByMagick: @"320x320#"];

5

[cf Chris] Para redimensionar para o tamanho desejado:

UIImage *after = [UIImage imageWithCGImage:before.CGImage
                                     scale:CGImageGetHeight(before.CGImage)/DESIREDHEIGHT
                               orientation:UIImageOrientationUp];

ou, equivalente, substituir CGImageGetWidth(...)/DESIREDWIDTH


Observe que os dados da imagem em si não são redimensionados, mas um fator de escala é aplicado, o que significa que a imagem tem o mesmo tamanho na memória.
inkredibl

5

Rogerio Chaves responde como uma extensão rápida

func scaledTo(size: CGSize) -> UIImage{
    UIGraphicsBeginImageContextWithOptions(size, false, 0.0);
    self.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
    let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()
    return newImage
}

E também bônus

func scaledTo(height: CGFloat) -> UIImage{
    let width = height*self.size.width/self.size.height
    return scaledTo(size: CGSize(width: width, height: height))
}

5

Swift 3.0 com opção à prova de falhas (retorna a imagem original em caso de erro):

func resize(image: UIImage, toSize size: CGSize) -> UIImage{
    UIGraphicsBeginImageContextWithOptions(size,false,1.0)
    image.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
    if let resizedImage = UIGraphicsGetImageFromCurrentImageContext() {
        UIGraphicsEndImageContext()
        return resizedImage
    }
    UIGraphicsEndImageContext()
    return image
}

5

(Compatível com Swift 4) solução iOS 10+ e iOS <10 (usando, UIGraphicsImageRendererse possível, UIGraphicsGetImageFromCurrentImageContextcaso contrário)

/// Resizes an image
///
/// - Parameter newSize: New size
/// - Returns: Resized image
func scaled(to newSize: CGSize) -> UIImage {
    let rect = CGRect(origin: .zero, size: newSize)

    if #available(iOS 10, *) {
        let renderer = UIGraphicsImageRenderer(size: newSize)
        return renderer.image { _ in
            self.draw(in: rect)
        }
    } else {
        UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
        self.draw(in: rect)
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage!
    }
}

5

Abordagem eficaz sem esticar a imagem Swift 4

// Method to resize image
func resize(image: UIImage, toScaleSize:CGSize) -> UIImage {
                UIGraphicsBeginImageContextWithOptions(toScaleSize, true, image.scale)
                        image.draw(in: CGRect(x: 0, y: 0, width: toScaleSize.width, height: toScaleSize.height))
                        let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
                        UIGraphicsEndImageContext()
                        return scaledImage!
                }

// método de chamada

    let resizedImage = self.resize(image: UIImage(named: "YourImageName")!, toScaleSize: CGSize(width: 290, height: 390))

3

A resposta de @Paul Lynch é ótima, mas isso mudaria a proporção da imagem. Se você não deseja alterar a proporção da imagem e ainda deseja que a nova imagem seja adequada ao novo tamanho, tente isso.

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {

// calculate a new size which ratio is same to original image
CGFloat ratioW = image.size.width / newSize.width;
CGFloat ratioH = image.size.height / newSize.height;

CGFloat ratio = image.size.width / image.size.height;

CGSize showSize = CGSizeZero;
if (ratioW > 1 && ratioH > 1) { 

    if (ratioW > ratioH) { 
        showSize.width = newSize.width;
        showSize.height = showSize.width / ratio;
    } else {
        showSize.height = newSize.height;
        showSize.width = showSize.height * ratio;
    }

} else if (ratioW > 1) {

    showSize.width = showSize.width;
    showSize.height = showSize.width / ratio;

} else if (ratioH > 1) {

    showSize.height = showSize.height;
    showSize.width = showSize.height * ratio;

}

//UIGraphicsBeginImageContext(newSize);
// In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
// Pass 1.0 to force exact pixel size.
UIGraphicsBeginImageContextWithOptions(showSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, showSize.width, showSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;}

3

use esta extensão

extension UIImage {
    public func resize(size:CGSize, completionHandler:(resizedImage:UIImage, data:NSData?)->()) {
        dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), { () -> Void in
            let newSize:CGSize = size
            let rect = CGRectMake(0, 0, newSize.width, newSize.height)
            UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
            self.drawInRect(rect)
            let newImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            let imageData = UIImageJPEGRepresentation(newImage, 0.5)
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                completionHandler(resizedImage: newImage, data:imageData)
            })
        })
    }
}

2

Swift 2.0:

let image = UIImage(named: "imageName")
let newSize = CGSize(width: 10, height: 10)

UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
image?.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height))
let imageResized = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

2

Aqui meu código Swift, um tanto detalhado

func scaleImage(image:UIImage,  toSize:CGSize) -> UIImage {
    UIGraphicsBeginImageContextWithOptions(toSize, false, 0.0);

    let aspectRatioAwareSize = self.aspectRatioAwareSize(image.size, boxSize: toSize, useLetterBox: false)


    let leftMargin = (toSize.width - aspectRatioAwareSize.width) * 0.5
    let topMargin = (toSize.height - aspectRatioAwareSize.height) * 0.5


    image.drawInRect(CGRectMake(leftMargin, topMargin, aspectRatioAwareSize.width , aspectRatioAwareSize.height))
    let retVal = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return retVal
}

func aspectRatioAwareSize(imageSize: CGSize, boxSize: CGSize, useLetterBox: Bool) -> CGSize {
    // aspect ratio aware size
    // http://stackoverflow.com/a/6565988/8047
    let imageWidth = imageSize.width
    let imageHeight = imageSize.height
    let containerWidth = boxSize.width
    let containerHeight = boxSize.height

    let imageAspectRatio = imageWidth/imageHeight
    let containerAspectRatio = containerWidth/containerHeight

    let retVal : CGSize
    // use the else at your own risk: it seems to work, but I don't know 
    // the math
    if (useLetterBox) {
        retVal = containerAspectRatio > imageAspectRatio ? CGSizeMake(imageWidth * containerHeight / imageHeight, containerHeight) : CGSizeMake(containerWidth, imageHeight * containerWidth / imageWidth)
    } else {
        retVal = containerAspectRatio < imageAspectRatio ? CGSizeMake(imageWidth * containerHeight / imageHeight, containerHeight) : CGSizeMake(containerWidth, imageHeight * containerWidth / imageWidth)
    }

    return retVal
}

11
Isso economizou meu tempo. Obrigado!
Quy Nguyen

atividades de back úteis, mas agem estranho depois segue e vindo, veja aqui plz: stackoverflow.com/questions/30978685/...
Ele Yifei何一非

Observe: Você pode querer adicionar uma pré-condição ( guard) à aspectRatioAwareSizefunção para que ela não gere uma exceção de divisão por zero se os parâmetros de entrada para imageSize ou boxSize forem #CGSize.zero
pkluz

@pkluz essa é uma boa sugestão, mas você precisaria deles em toda a função, acho, já que cada variável é o divisor de outra linha ... Vou deixar isso como para maior clareza. Para produção ou uma biblioteca para o github, você pode querer reforçar isso ... Mais do que um guarda, estou preocupado que a função use nomes curtos de variáveis ​​que não dizem muito. Eu provavelmente consertaria isso primeiro e depois adicionaria guardas para outras coisas, e depois adicionaria testes de unidade.
Dan Rosenstark

2

Resposta rápida 4:

func scaleDown(image: UIImage, withSize: CGSize) -> UIImage {
    let scale = UIScreen.main.scale
    UIGraphicsBeginImageContextWithOptions(withSize, false, scale)
    image.draw(in: CGRect(x: 0, y: 0, width: withSize.width, height: withSize.height))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage!
}

Não há necessidade de definir a escala real:The scale factor to apply to the bitmap. If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen.
nathan

1

Descobri que é difícil encontrar uma resposta que você possa usar imediatamente no seu projeto Swift 3 . O principal problema de outras respostas é que elas não respeitam o canal alfa da imagem. Aqui está a técnica que estou usando em meus projetos.

extension UIImage {

    func scaledToFit(toSize newSize: CGSize) -> UIImage {
        if (size.width < newSize.width && size.height < newSize.height) {
            return copy() as! UIImage
        }

        let widthScale = newSize.width / size.width
        let heightScale = newSize.height / size.height

        let scaleFactor = widthScale < heightScale ? widthScale : heightScale
        let scaledSize = CGSize(width: size.width * scaleFactor, height: size.height * scaleFactor)

        return self.scaled(toSize: scaledSize, in: CGRect(x: 0.0, y: 0.0, width: scaledSize.width, height: scaledSize.height))
    }

    func scaled(toSize newSize: CGSize, in rect: CGRect) -> UIImage {
        if UIScreen.main.scale == 2.0 {
            UIGraphicsBeginImageContextWithOptions(newSize, !hasAlphaChannel, 2.0)
        }
        else {
            UIGraphicsBeginImageContext(newSize)
        }

        draw(in: rect)
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage ?? UIImage()
    }

    var hasAlphaChannel: Bool {
        guard let alpha = cgImage?.alphaInfo else {
            return false
        }
        return alpha == CGImageAlphaInfo.first ||
            alpha == CGImageAlphaInfo.last ||
            alpha == CGImageAlphaInfo.premultipliedFirst ||
            alpha == CGImageAlphaInfo.premultipliedLast
    }
}

Exemplo de uso:

override func viewDidLoad() {
    super.viewDidLoad()

    let size = CGSize(width: 14.0, height: 14.0)
    if let image = UIImage(named: "barbell")?.scaledToFit(toSize: size) {
        let imageView = UIImageView(image: image)
        imageView.center = CGPoint(x: 100, y: 100)
        view.addSubview(imageView)
    }
}

Este código é uma reescrita da extensão da Apple, com suporte adicional para imagens com e sem canal alfa.

Como leitura adicional, recomendo verificar este artigo para diferentes técnicas de redimensionamento de imagem. A abordagem atual oferece desempenho decente, opera APIs de alto nível e fácil de entender. Eu recomendo segui-lo, a menos que você ache que o redimensionamento de imagem é um gargalo no seu desempenho.


1

Use esta extensão, caso precise redimensionar a largura / altura apenas com a proporção.

extension UIImage {
    func resize(to size: CGSize) -> UIImage {
        return UIGraphicsImageRenderer(size: size).image { _ in
            draw(in: CGRect(origin: .zero, size: size))
        }
    }
    func resize(width: CGFloat) -> UIImage {
        return resize(to: CGSize(width: width, height: width / (size.width / size.height)))
    }
    func resize(height: CGFloat) -> UIImage {
        return resize(to: CGSize(width: height * (size.width / size.height), height: height))
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.