Redimensionner UIImage en conservant le rapport hauteur / largeur et la largeur

136

J'ai vu dans de nombreux articles pour redimensionner l'image en gardant les proportions. Ces fonctions utilisent les points fixes (Largeur et Hauteur) pour RECT lors du redimensionnement. Mais dans mon projet, je dois redimensionner la vue en fonction de la largeur seule, la hauteur doit être prise automatiquement en fonction du rapport hauteur / largeur. n'importe qui m'aide à y parvenir.

Jasmin
la source

Réponses:

228

La méthode de Srikar fonctionne très bien, si vous connaissez à la fois la hauteur et la largeur de votre nouvelle taille. Si, par exemple, vous ne connaissez que la largeur à laquelle vous souhaitez mettre à l'échelle et que vous ne vous souciez pas de la hauteur, vous devez d'abord calculer le facteur d'échelle de la hauteur.

+(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;
}
Maverick1er
la source
22
Vous n'avez pas vraiment besoin de la newWidthvariable ici, c'est déjà i_width.
Matthew Quiros
2
Vous devez également diviser par hauteur et comparer les facteurs d'échelle en utilisant celui qui est le plus proche de 0. le code ci-dessus ne fonctionnera qu'avec des images plus larges que hautes
IceForge
1
J'aurais juste besoin de diviser par hauteur si je voulais également connaître mon rapport de hauteur et l'ajuster en mettant à l'échelle la largeur ou la hauteur. mais puisque le TO a demandé explicitement de garder le rapport et la largeur, et non la largeur ou la hauteur, ma réponse est tout à fait correcte.
Maverick1er
12
Excellente réponse, merci. Une dernière chose: si vous rencontrez une perte de qualité d'image en utilisant cette méthode, utilisez UIGraphicsBeginImageContextWithOptions(CGSizeMake(newWidth, newHeight), NO, 0);plutôtUIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight));
skornos
57

Si vous ne savez pas si l'image sera portrait ou paysage (par exemple, l'utilisateur prend une photo avec un appareil photo), j'ai créé une autre méthode qui prend les paramètres de largeur et de hauteur maximum.

Disons que vous avez un UIImage *myLargeImagerapport 4: 3.

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

La UIImage redimensionnée sera 1024x768 si paysage; 768x1024 si portrait. Cette méthode générera également des images de résolution plus élevée pour l'affichage de la rétine.

+ (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
la source
1
C'est la meilleure chose, mais cela retourne toujours le double de ma taille définie
Mashhadi
2
Si vous ne voulez que réduire la taille, pas augmenter la taille, n'oubliez pas de démarrer la méthode imageWithImage: scaledToMaxWidth: maxHeight: avec: if (image.size.width <width && image.size.height <height) {return image; }
Arjan
5
Pour tenir dans une largeur max et hauteur max, vous avez besoin du ratio minimum comme le facteur d'échelle: min(ratioX, ratioY). Sinon, si la largeur est plus grande, elle peut ne pas correspondre à la hauteur maximale.
Jason Sturges
ImageUtilities où il est?
tong
42

Meilleure réponse Maverick 1st est correctement traduite en Swift (fonctionne avec le dernier 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
la source
38

merci @ Maverick1st l'algorithme, je l'ai implémenté Swift, dans mon cas, la hauteur est le paramètre d'entrée

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
la source
18

Cette méthode est une catégorie sur UIImage. S'adapte à quelques lignes de code à l'aide d'AVFoundation. N'oubliez pas d'importer #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
la source
2
J'ai trouvé cela pour me donner les meilleurs résultats avec la plus petite empreinte mémoire. +1
Tommie C.
11

Version Swift 5 d'ajustement de l'aspect à la hauteur , basée sur la réponse de @ János

Utilise l' UIGraphicsImageRendererAPI moderne , donc un UIImageretour valide est garanti.

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

Vous pouvez l'utiliser en conjonction avec un élément d'image PDF (vectoriel), pour préserver la qualité quelle que soit la taille de rendu.

Womble
la source
7

Le moyen le plus simple consiste à définir le cadre de votre UIImageViewet à définir l' contentModeune des options de redimensionnement.

Le code va comme ceci - Cela peut être utilisé comme une méthode utilitaire -

+ (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
la source
Gardez à l'esprit que l'OP n'a pas mentionné d'UIImageView. Au lieu de cela, il voulait vraisemblablement ajuster l'aspect d'une [copie d'une] UIImage directement, ce qui est une chose très pratique à avoir lorsque vous travaillez avec CoreGraphics.
Womble
6

Par programme:

imageView.contentMode = UIViewContentModeScaleAspectFit;

Storyboard:

entrez la description de l'image ici

Mona
la source
cela fonctionne, MAIS en réduisant les empreintes mémoire, vous pouvez faire fonctionner l'application plus rapidement, par exemple en chargeant beaucoup de vignettes.
ingconti
C'est une UIImageView, pas une UIImage. Parfois, vous devez dessiner sans UIImageView.
Womble
5

Eh bien, nous avons quelques bonnes réponses ici pour redimensionner l'image, mais je modifie simplement selon mes besoins. J'espère que cela aidera quelqu'un comme moi.

Ma condition était

  1. Si la largeur de l'image est supérieure à 1920, redimensionnez-la avec une largeur de 1920 et en maintenant la hauteur avec le rapport hauteur / largeur d'origine.
  2. Si la hauteur de l'image est supérieure à 1080, redimensionnez-la avec une hauteur de 1080 et en conservant la largeur avec le rapport hauteur / largeur d'origine.

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

Merci à @ Srikar Appal , pour le redimensionnement, j'ai utilisé sa méthode.

Vous pouvez également vérifier cela pour le calcul de redimensionnement.

swiftBoy
la source
4

Importez simplement AVFoundation et utilisez AVMakeRectWithAspectRatioInsideRect(CGRectCurrentSize, CGRectMake(0, 0, YOUR_WIDTH, CGFLOAT_MAX)

mariusLAN
la source
2

Pour améliorer la réponse 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
la source
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
    }
}

Lorsque l'échelle est définie sur 0,0, le facteur d'échelle de l'écran principal est utilisé, qui pour les écrans Retina est de 2,0 ou plus (3,0 sur l'iPhone 6 Plus).

Oscarr
la source
1

Solution de Ryan @Ryan en code SWIFT

utilisation:

 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
la source
1

Dans Swift 3, il y a quelques changements. Voici une extension de 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
la source
1

Zeeshan Tufail et Womble répondent dans Swift 5 avec de petites améliorations. Ici, nous avons une extension avec 2 fonctions pour redimensionner l'image maxLengthde n'importe quelle dimension et la compression 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
la source
0

Celui-ci était parfait pour moi. Conserve le rapport hauteur / largeur et prend un maxLength. La largeur ou la hauteur ne dépassera pasmaxLength

-(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
la source
0

Calcule la meilleure hauteur de l'image pour la largeur disponible.

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
la source
-1

Je l'ai résolu d'une manière beaucoup plus simple

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

Le multiplicateur de l'échelle définira l'écaillage de l'image, plus le multiplicateur est petit l'image. afin que vous puissiez vérifier ce qui convient à votre écran.

Ou vous pouvez également obtenir le multiplicateur en divisant la largeur de l'image / la largeur de l'écran.

Siddhant Saxena
la source