Redimensione a UIImage mantendo a proporção e a largura

136

Eu vi em muitos posts para redimensionar a imagem mantendo a proporção. Essas funções usam os pontos fixos (Largura e Altura) para RECT durante o redimensionamento. Mas, no meu projeto, preciso redimensionar a exibição com base apenas na largura. A altura deve ser obtida automaticamente com base na proporção. alguém me ajuda a conseguir isso.

Jasmim
fonte

Respostas:

228

O método Srikar funciona muito bem, se você conhece a altura e a largura do seu novo tamanho. Se, por exemplo, você conhece apenas a largura para a qual deseja escalar e não se importa com a altura, primeiro precisa calcular o fator de escala da altura.

+(UIImage*)imageWithImage: (UIImage*) sourceImage scaledToWidth: (float) i_width
{
    float oldWidth = sourceImage.size.width;
    float scaleFactor = i_width / oldWidth;

    float newHeight = sourceImage.size.height * scaleFactor;
    float newWidth = oldWidth * scaleFactor;

    UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight));
    [sourceImage drawInRect:CGRectMake(0, 0, newWidth, newHeight)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
    UIGraphicsEndImageContext();
    return newImage;
}
Maverick1st
fonte
22
Você realmente não precisa da newWidthvariável aqui, porém, já é i_width.
Matthew Quiros
2
Você precisa dividir pela altura bem e comparar os fatores de escala usando o que for mais perto de 0. O código acima irá funcionar apenas com imagens que são mais largas do que eles são altos
IceForge
1
Eu só precisaria dividir pela altura, se quisesse conhecer também a minha proporção de altura e ajustá-la ao escalar a largura ou a altura. mas como o OT pediu explicitamente para manter proporção e largura, e não largura ou altura, minha resposta está totalmente correta.
Maverick1st
12
Ótima resposta, obrigado. Só mais uma coisa: se você experimentar qualquer perda de qualidade de imagem usando este método, em seguida, usar UIGraphicsBeginImageContextWithOptions(CGSizeMake(newWidth, newHeight), NO, 0);um pouco entãoUIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight));
skornos
57

Se você não souber se a imagem será retrato ou paisagem (por exemplo, o usuário tira uma foto com a câmera), criei outro método que utiliza parâmetros máximos de largura e altura.

Digamos que você tenha uma UIImage *myLargeImageproporção de 4: 3.

UIImage *myResizedImage = [ImageUtilities imageWithImage:myLargeImage 
                                        scaledToMaxWidth:1024 
                                               maxHeight:1024];

A UIImage redimensionada será 1024x768 se paisagem; 768x1024 se retrato. Este método também irá gerar imagens de alta resolução para a exibição da retina.

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

    return newImage;
}

+ (UIImage *)imageWithImage:(UIImage *)image scaledToMaxWidth:(CGFloat)width maxHeight:(CGFloat)height {
    CGFloat oldWidth = image.size.width;
    CGFloat oldHeight = image.size.height;

    CGFloat scaleFactor = (oldWidth > oldHeight) ? width / oldWidth : height / oldHeight;

    CGFloat newHeight = oldHeight * scaleFactor;
    CGFloat newWidth = oldWidth * scaleFactor;
    CGSize newSize = CGSizeMake(newWidth, newHeight);

    return [ImageUtilities imageWithImage:image scaledToSize:newSize];
}
Ryan
fonte
1
Esta é a melhor coisa, mas sempre voltar dobro do meu tamanho definido
Mashhadi
2
Se você deseja apenas reduzir, não aumentar, não se esqueça de iniciar o método imageWithImage: scaledToMaxWidth: maxHeight: with: if (image.size.width <width && image.size.height <height) {return image; }
Arjan 6/15
5
Para caber dentro de uma largura máxima e altura máxima, é necessário o rácio mínimo como o fator de escala: min(ratioX, ratioY). Caso contrário, se a largura for maior, pode não caber na altura máxima.
Jason Sturges
ImageUtilities onde está?
tong
42

Melhor resposta que o Maverick 1st foi traduzido corretamente para Swift (trabalhando com o mais recente swift 3 ):

func imageWithImage (sourceImage:UIImage, scaledToWidth: CGFloat) -> UIImage {
    let oldWidth = sourceImage.size.width
    let scaleFactor = scaledToWidth / oldWidth

    let newHeight = sourceImage.size.height * scaleFactor
    let newWidth = oldWidth * scaleFactor

    UIGraphicsBeginImageContext(CGSize(width:newWidth, height:newHeight))
    sourceImage.draw(in: CGRect(x:0, y:0, width:newWidth, height:newHeight))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage!
}
Alessandro Ornano
fonte
38

obrigado @ Maverick1st o algoritmo, eu o implementei Swift, no meu caso height é o parâmetro de entrada

class func resizeImage(image: UIImage, newHeight: CGFloat) -> UIImage {

    let scale = newHeight / image.size.height
    let newWidth = image.size.width * scale
    UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight))
    image.drawInRect(CGRectMake(0, 0, newWidth, newHeight))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage
}
János
fonte
18

Este método é uma categoria no UIImage. É dimensionado para caber em algumas linhas de código usando o AVFoundation. Não esqueça de importar #import <AVFoundation/AVFoundation.h>.

@implementation UIImage (Helper)

- (UIImage *)imageScaledToFitToSize:(CGSize)size
{
    CGRect scaledRect = AVMakeRectWithAspectRatioInsideRect(self.size, CGRectMake(0, 0, size.width, size.height));
    UIGraphicsBeginImageContextWithOptions(size, NO, 0);
    [self drawInRect:scaledRect];
    UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return scaledImage;
}

@end
mohamede1945
fonte
2
Encontrei isso para fornecer os melhores resultados para mim com menor espaço de memória. +1
Tommie C.
11

Versão Swift 5 do tamanho do aspecto , com base na resposta de @ János

Usa a UIGraphicsImageRendererAPI moderna , portanto, UIImageé garantido que um válido retorne.

extension UIImage
{
    /// Given a required height, returns a (rasterised) copy
    /// of the image, aspect-fitted to that height.

    func aspectFittedToHeight(_ newHeight: CGFloat) -> UIImage
    {
        let scale = newHeight / self.size.height
        let newWidth = self.size.width * scale
        let newSize = CGSize(width: newWidth, height: newHeight)
        let renderer = UIGraphicsImageRenderer(size: newSize)

        return renderer.image { _ in
            self.draw(in: CGRect(origin: .zero, size: newSize))
        }
    }
}

Você pode usar isso em conjunto com um recurso de imagem PDF (baseado em vetor), para preservar a qualidade em qualquer tamanho de renderização.

Womble
fonte
7

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

O código fica assim - Isso pode ser usado como um método utilitário -

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize
{
    UIGraphicsBeginImageContext(newSize);
    [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
    UIGraphicsEndImageContext();
    return newImage;
}
Srikar Appalaraju
fonte
Lembre-se de que o OP não mencionou um UIImageView. Em vez disso, ele provavelmente pretendia ajustar diretamente uma [cópia de uma] UIImage, o que é uma coisa muito útil quando você está trabalhando no CoreGraphics.
Womble
6

Programaticamente:

imageView.contentMode = UIViewContentModeScaleAspectFit;

Storyboard:

insira a descrição da imagem aqui

Mona
fonte
funciona, mas reduzindo as pegadas de memória, você pode fazer o aplicativo funcionar mais rápido, por exemplo, carregando muitas miniaturas.
ingconti
Essa é uma UIImageView, não uma UIImage. Às vezes, você precisa desenhar sem um UIImageView.
Womble
5

Bem, temos algumas boas respostas aqui para redimensionar a imagem, mas apenas modifico conforme minha necessidade. espero que isso ajude alguém como eu.

Minha exigência era

  1. Se a largura da imagem for maior que 1920, redimensione-a com largura 1920 e mantendo a altura com a proporção original.
  2. Se a altura da imagem for superior a 1080, redimensione-a com altura 1080 e mantendo a largura com a proporção original.

 if (originalImage.size.width > 1920)
 {
        CGSize newSize;
        newSize.width = 1920;
        newSize.height = (1920 * originalImage.size.height) / originalImage.size.width;
        originalImage = [ProfileEditExperienceViewController imageWithImage:originalImage scaledToSize:newSize];        
 }

 if (originalImage.size.height > 1080)
 {
            CGSize newSize;
            newSize.width = (1080 * originalImage.size.width) / originalImage.size.height;
            newSize.height = 1080;
            originalImage = [ProfileEditExperienceViewController imageWithImage:originalImage scaledToSize:newSize];

 }

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize
{
        UIGraphicsBeginImageContext(newSize);
        [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
}

Obrigado a @ Srikar Appal , por redimensionar eu usei o método dele.

Você também pode verificar isso para calcular o redimensionamento.

swiftBoy
fonte
4

Basta importar o AVFoundation e usar AVMakeRectWithAspectRatioInsideRect(CGRectCurrentSize, CGRectMake(0, 0, YOUR_WIDTH, CGFLOAT_MAX)

mariusLAN
fonte
2

Para melhorar a resposta de Ryan:

   + (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)size {
CGFloat oldWidth = image.size.width;
        CGFloat oldHeight = image.size.height;

//You may need to take some retina adjustments into consideration here
        CGFloat scaleFactor = (oldWidth > oldHeight) ? width / oldWidth : height / oldHeight;

return [UIImage imageWithCGImage:image.CGImage scale:scaleFactor orientation:UIImageOrientationUp];
    }
TheCodingArt
fonte
2
extension UIImage {

    /// Returns a image that fills in newSize
    func resizedImage(newSize: CGSize) -> UIImage? {
        guard size != newSize else { return self }

    let hasAlpha = false
    let scale: CGFloat = 0.0
    UIGraphicsBeginImageContextWithOptions(newSize, !hasAlpha, scale)
        UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)

        draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
        let newImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage
    }

    /// Returns a resized image that fits in rectSize, keeping it's aspect ratio
    /// Note that the new image size is not rectSize, but within it.
    func resizedImageWithinRect(rectSize: CGSize) -> UIImage? {
        let widthFactor = size.width / rectSize.width
        let heightFactor = size.height / rectSize.height

        var resizeFactor = widthFactor
        if size.height > size.width {
            resizeFactor = heightFactor
        }

        let newSize = CGSize(width: size.width / resizeFactor, height: size.height / resizeFactor)
        let resized = resizedImage(newSize: newSize)

        return resized
    }
}

Quando a escala é definida como 0,0, é usado o fator de escala da tela principal, que para os displays Retina é 2.0 ou superior (3.0 no iPhone 6 Plus).

oscarr
fonte
1

Ryan's Solution @Ryan em código rápido

usar:

 func imageWithSize(image: UIImage,size: CGSize)->UIImage{
    if UIScreen.mainScreen().respondsToSelector("scale"){
        UIGraphicsBeginImageContextWithOptions(size,false,UIScreen.mainScreen().scale);
    }
    else
    {
         UIGraphicsBeginImageContext(size);
    }

    image.drawInRect(CGRectMake(0, 0, size.width, size.height));
    var newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}

//Summon this function VVV
func resizeImageWithAspect(image: UIImage,scaledToMaxWidth width:CGFloat,maxHeight height :CGFloat)->UIImage
{
    let oldWidth = image.size.width;
    let oldHeight = image.size.height;

    let scaleFactor = (oldWidth > oldHeight) ? width / oldWidth : height / oldHeight;

    let newHeight = oldHeight * scaleFactor;
    let newWidth = oldWidth * scaleFactor;
    let newSize = CGSizeMake(newWidth, newHeight);

    return imageWithSize(image, size: newSize);
}
Daniel Krom
fonte
1

No Swift 3, existem algumas mudanças. Aqui está uma extensão para UIImage:

public extension UIImage {
    public func resize(height: CGFloat) -> UIImage? {
        let scale = height / self.size.height
        let width = self.size.width * scale
        UIGraphicsBeginImageContext(CGSize(width: width, height: height))
        self.draw(in: CGRect(x:0, y:0, width:width, height:height))
        let resultImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return resultImage
    }
}
Eridana
fonte
1

Zeeshan Tufail e Womble respondem no Swift 5 com pequenas melhorias. Aqui temos extensão com 2 funções para dimensionar a imagem para maxLengthqualquer dimensão e compactação jpeg.

extension UIImage {
    func aspectFittedToMaxLengthData(maxLength: CGFloat, compressionQuality: CGFloat) -> Data {
        let scale = maxLength / max(self.size.height, self.size.width)
        let format = UIGraphicsImageRendererFormat()
        format.scale = scale
        let renderer = UIGraphicsImageRenderer(size: self.size, format: format)
        return renderer.jpegData(withCompressionQuality: compressionQuality) { context in
            self.draw(in: CGRect(origin: .zero, size: self.size))
        }
    }
    func aspectFittedToMaxLengthImage(maxLength: CGFloat, compressionQuality: CGFloat) -> UIImage? {
        let newImageData = aspectFittedToMaxLengthData(maxLength: maxLength, compressionQuality: compressionQuality)
        return UIImage(data: newImageData)
    }
}
Dmih
fonte
0

Este foi perfeito para mim. Mantém a proporção e leva a maxLength. Largura ou altura não será superior amaxLength

-(UIImage*)imageWithImage: (UIImage*) sourceImage maxLength: (float) maxLength
{
    CGFloat scaleFactor = maxLength / MAX(sourceImage.size.width, sourceImage.size.height);

    float newHeight = sourceImage.size.height * scaleFactor;
    float newWidth = sourceImage.size.width * scaleFactor;

    UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight));
    [sourceImage drawInRect:CGRectMake(0, 0, newWidth, newHeight)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}
Zeeshan Tufail
fonte
0

Calcula a melhor altura da imagem para a largura disponível.

import Foundation

public extension UIImage {
    public func height(forWidth width: CGFloat) -> CGFloat {
        let boundingRect = CGRect(
            x: 0,
            y: 0,
            width: width,
            height: CGFloat(MAXFLOAT)
        )
        let rect = AVMakeRect(
            aspectRatio: size,
            insideRect: boundingRect
        )
        return rect.size.height
    }
}
AshvinGudaliya
fonte
-1

Eu resolvi isso de uma maneira muito mais simples

UIImage *placeholder = [UIImage imageNamed:@"OriginalImage.png"];
self.yourImageview.image = [UIImage imageWithCGImage:[placeholder CGImage] scale:(placeholder.scale * 1.5)
                                  orientation:(placeholder.imageOrientation)];

O multiplicador da escala definirá a escaldadura da imagem, mais o multiplicador menor a imagem. para que você possa verificar o que se ajusta à sua tela.

Ou você também pode obter o multiplicador dividindo largura de imagem / largura de tela.

Siddhant Saxena
fonte