La façon la plus simple de redimensionner un UIImage?

397

Dans mon application iPhone, je prends une photo avec l'appareil photo, puis je veux la redimensionner à 290 * 390 pixels. J'utilisais cette méthode pour redimensionner l'image:

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

Cela fonctionne parfaitement, mais c'est une fonction non documentée, donc je ne peux plus l'utiliser avec l'iPhone OS4.

Alors ... quelle est la façon la plus simple de redimensionner un UIImage?

pimpampoum
la source
La façon de le faire en 2019, nshipster.com/image-resizing
dengST30

Réponses:

772

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

Ou vous pouvez utiliser cette méthode utilitaire, si vous avez réellement besoin de redimensionner une image:

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

Exemple d'utilisation:

#import "MYUtil.h"

UIImage *myIcon = [MYUtil imageWithImage:myUIImageInstance scaledToSize:CGSizeMake(20, 20)];
Paul Lynch
la source
11
Je ne conserverais PAS le résultat ici. newImagesera déjà libéré automatiquement, et il devrait jusqu'à la méthode qui a appelé cela pour le conserver ou non. Cette approche ne fait que demander un bogue de mémoire car elle initne fait pas partie du nom de la méthode. Je m'attendrais à ce qu'une méthode nommée comme ceci retourne un objet libéré automatiquement.
Alex Wayne
Point valide et modifié. Je vais juste laisser la prudence que mon code qui utilise cela génère des erreurs malloc ailleurs sans retenue supplémentaire, ce qui est clairement ma faute en quelque sorte :-).
Paul Lynch,
26
Depuis iOS 4.0 , les fonctions UIGraphics * sont toutes thread-safe.
BJ Homer, le
3
@Paul Lynch: Gardez à l'esprit que ce changement rompt la mission du message d'origine: comment redimensionner une image à une taille de pixel exacte (par opposition à la taille en points ).
Nikolai Ruhe
8
Pour ceux qui éprouvent des difficultés en raison de la limitation indiquée par @NikolaiRuhe, vous pouvez le forcer à une taille de pixel exacte en passant 1,0 au lieu de 0,0 dans l'appel àUIGraphicsBeginImageContextWithOptions
Danny
84

Voici une version Swift de la réponse 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
}

Et en extension:

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()
    }
}
Rogerio Chaves
la source
2
vient de noter que, dans iOS, votre nouvelle taille sera multiple de 2x, 3x en fonction de l'appareil
Sruit A.Suk
6
UIGraphicsBeginImageContextWithOptions (newSize, false, image.scale); pour tous les appareils
phnmnn
Notez que cela ne redimensionne pas le CGImage sous-jacent (au moins dans iOS 12.1). Ce qui est correct en fonction de la question, mais si vous écrivez l'image, vous devrez utiliser le cgImage et vous obtiendrez l'image d'origine.
prewett
72

Bonne solution Swift 3.0 pour iOS 10+ : ImageRenderersyntaxe d' utilisation et de fermeture:

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

Et voici la version 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
Jakub Průša
la source
1
Solution très élégante, cela devrait être la réponse acceptée!
appsunited
2
C'est mieux qu'élégant, c'est correct. Les documents api d'Apple conseillent un nouveau code pour utiliser UIGraphicsRenderer comme le fait ce code. Merci
Paul Bruneau
1
@appsunited La question a presque 8 ans. Et cette méthode ne fonctionne que pour iOS10 + comme indiqué.
Lorsque vous copiez le nouvel objet image dans le presse-papiers, cela produit un bloc blanc pour moi. Ce n'est pas vraiment redimensionner la vue de l'image?
Oliver Dixon
1
cette fonction renvoyant une image de 0 hauteur et 0 largeur, attendez-vous à une hauteur de 9000 et une largeur de 9000.
Krishan Kumar
31

Trevor Howard a quelques catégories UIImage qui gèrent très bien le redimensionnement. Si rien d'autre, vous pouvez utiliser le code comme exemples.

Remarque: depuis iOS 5.1, cette réponse n'est peut-être pas valide. Voir commentaire ci-dessous.

TechZen
la source
2
J'ai également dû redimensionner certaines images, et j'ai d'abord essayé les ajouts de Trevor à UIImage, mais j'ai eu quelques bugs étranges sur les PNG (quelque chose sur le canal alpha). La réponse acceptée à cette question a bien fonctionné cependant.
Sorig
1
Ce n'est plus valable (au moins avec iOS5.1
Jann
@Jann, regardez les commits sous le post.
vikingosegundo
@Jann Si vous suivez les correctifs suggérés dans les commentaires, allez avec "Matt dit: 22 novembre 2011 à 17h39" car il libère le colorspaceref (contrairement à la solution similaire de horseshoe7).
Ben Flynn
1
@sanmai utilisant l'une de ces méthodes de redimensionnement, les images perdent leur netteté. est-il possible d'atteindre la netteté
vamsi575kg
31

Une version plus compacte pour Swift 4 et iOS 10+:

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

Usage:

let resizedImage = image.resized(to: CGSize(width: 50, height: 50))
neave
la source
26

J'ai également vu cela aussi (que j'utilise UIButtonspour l'état Normal et Sélectionné car les boutons ne resizeconviennent pas). Le mérite revient à celui qui était l'auteur original.

Créez d'abord un fichier .h et .m vide appelé UIImageResizing.hetUIImageResizing.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

Incluez ce fichier .h dans le fichier .m dans lequel vous allez utiliser la fonction, puis appelez-le comme ceci:

UIImage* image = [UIImage imageNamed:@"largeImage.png"];
UIImage* smallImage = [image scaleToSize:CGSizeMake(100.0f,100.0f)];
J'ai été volé
la source
Cela ne copie pas l'orientation de l'image.
Kevin
20

Cette amélioration du code de Paul vous donnera une image haute résolution nette sur un iPhone avec un écran rétine. Sinon, lors de la réduction, c'est flou.

+ (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;
}
malhal
la source
13
Juste pour info, les modifications que vous avez apportées sont inutiles, car la fourniture d'une valeur de 0,0 pour l'échelle dans UIGraphicsBeginImageContextWithOptionsutilisera automatiquement l'échelle de l'écran principal (à partir d'iOS 3.2).
Andrew R.
16

Voici un moyen simple:

    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 est une nouvelle image.

asmad
la source
16

Solution Swift pour Stretch Fill, Aspect Fill et 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()
    }
}

et pour l'utiliser, vous pouvez faire ce qui suit:

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

Merci à abdullahselek pour sa solution originale.

Josh Bernfeld
la source
15

Voici une modification de la catégorie écrite par iWasRobbed ci-dessus. Il conserve le rapport hauteur / largeur de l'image d'origine au lieu de la déformer.

- (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;
}
Scott signifie
la source
11

Si vous voulez juste une image plus petite et ne vous souciez pas de la taille exacte:

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

Définition de l'échelle sur 0.25f vous donnera une image de 816 x 612 à partir d'un appareil photo 8MP.

Voici une catégorie UIImage + Scale pour ceux qui en ont besoin.

sanmai
la source
10

Pourquoi si compliqué? Je pense que l'utilisation de l'API système peut atteindre le même résultat:

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.

Le seul problème est que vous devez passer l'inverse du rapport cible comme deuxième argument, car selon le document, le deuxième paramètre spécifie le rapport de l'image d'origine par rapport à la nouvelle échelle.

Chris
la source
Parce que l'échelle (%) n'est pas la même chose que les pixels (w / h).
L'échelle doit être largeImage.scale / ratio et non 1 / ratio.
Marián Černý
9

Il s'agit d'une extension UIImage compatible avec Swift 3 et Swift 4 qui redimensionne l'image à une taille donnée avec un rapport d'aspect

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

}

Exemple d'utilisation

let image = UIImage(named: "apple")
let scaledImage = image.scaleImageToFitSize(size: CGSize(width: 45.0, height: 45.0))
abdullahselek
la source
Je recommanderais de nommer la fonction scaledImage(with size:)ou scaledWithSize(_:). Aussi UIGraphicsGetImageFromCurrentImageContextne peut vraiment revenir nil, donc !fonctionnerait aussi bien. Vous pouvez également simplifier la création de rect en CGRect(origin: .zero, size: size).
Sulthan
8

J'ai trouvé une catégorie pour UIImage dans les propres exemples d'Apple qui fait le même tour. Voici le lien: https://developer.apple.com/library/ios/samplecode/sc2273/Listings/AirDropSample_UIImage_Resize_m.html .

Vous n'aurez qu'à changer l'appel:

UIGraphicsBeginImageContextWithOptions(newSize, YES, 2.0);

en imageWithImage:scaledToSize:inRect:avec:

UIGraphicsBeginImageContextWithOptions(newSize, NO, 2.0);

Afin de considérer le canal alpha dans l'image.

aubykhan
la source
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
}
ashwini
la source
C'est une bonne idée d'ajouter du texte descriptif pour accompagner votre code.
GMchris
6

Pour mes collègues Xamarians, voici une version Xamarin.iOS C # 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;
}
Martin Zikmund
la source
5

Si vous souhaitez créer une vignette d'un UIImage (avec redimensionnement proportionnel ou peut-être un recadrage impliqué), consultez la catégorie UIImage + Resize qui vous permet d'utiliser une syntaxe concise de type ImageMagick:

UIImage* squareImage       = [image resizedImageByMagick: @"320x320#"];
Vlad Andersen
la source
5

[cf Chris] Pour redimensionner à la taille souhaitée:

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

ou, de manière équivalente, remplacer CGImageGetWidth(...)/DESIREDWIDTH

tiritea
la source
Notez que les données d'image elles-mêmes ne sont pas redimensionnées, mais un facteur d'échelle est appliqué, ce qui signifie que l'image est de la même taille en mémoire.
inkredibl
5

Rogerio Chaves répond comme une extension rapide

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
}

Et aussi des bonus

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

Swift 3.0 avec option de sécurité intégrée (renvoie l'image d'origine en cas d'erreur):

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
}
molette
la source
5

(Compatible avec Swift 4) iOS 10+ et iOS <10 (en utilisant UIGraphicsImageRenderersi possible, UIGraphicsGetImageFromCurrentImageContextsinon)

/// 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!
    }
}
nathan
la source
5

Approche efficace sans étirer l'image 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éthode d'appel

    let resizedImage = self.resize(image: UIImage(named: "YourImageName")!, toScaleSize: CGSize(width: 290, height: 390))
Équipe iOS
la source
3

La réponse de @Paul Lynch est excellente, mais cela changerait le rapport d'image. si vous ne souhaitez pas modifier le rapport d'image et que vous souhaitez toujours que la nouvelle image soit adaptée à la nouvelle taille, essayez ceci.

+ (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;}
wossoneri
la source
3

utiliser cette extension

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)
            })
        })
    }
}
Beslan Tularov
la source
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()
Phil
la source
2

Voici mon code Swift quelque peu bavard

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
}
Dan Rosenstark
la source
1
Cela m'a fait gagner du temps. Merci!
Quy Nguyen
utile, mais agissez bizarrement après les activités d'enchaînement et de retour, voir ici plz: stackoverflow.com/questions/30978685/…
He Yifei 何 一 非
Remarque: vous souhaiterez peut-être ajouter une précondition ( guard) à la aspectRatioAwareSizefonction afin qu'elle ne génère pas d'exception de division par zéro si les paramètres d'entrée pour imageSize ou boxSize sontCGSize.zero
pkluz
@pkluz c'est une bonne suggestion, mais vous en auriez besoin partout dans la fonction, je pense, puisque chaque variable est le diviseur d'une autre ligne ... Je vais laisser cela comme pour la clarté. Pour la production ou une bibliothèque pour github, vous voudrez peut-être renforcer cela ... Plus qu'un gardien, je crains que la fonction utilise des noms de variables courts qui ne disent pas grand-chose. Je corrigerais probablement cela en premier, puis ajouterais des gardes pour d'autres choses, puis ajouterais des tests unitaires.
Dan Rosenstark
2

Réponse de Swift 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!
}
DanielEdrisian
la source
Pas besoin de définir l'échelle réelle: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

J'ai découvert qu'il est difficile de trouver une réponse que vous pouvez utiliser prête à l'emploi dans votre projet Swift 3 . Le principal problème des autres réponses est qu'elles n'honorent pas le canal alpha de l'image. Voici la technique que j'utilise dans mes projets.

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

Exemple d'utilisation:

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

Ce code est une réécriture de l'extension d' Apple avec un support supplémentaire pour les images avec et sans canal alpha.

Comme lecture supplémentaire, je recommande de consulter cet article pour différentes techniques de redimensionnement d'image. L'approche actuelle offre des performances décentes, elle exploite des API de haut niveau et faciles à comprendre. Je vous recommande de vous y tenir, sauf si vous trouvez que le redimensionnement de l'image est un goulot d'étranglement dans vos performances.

Vadim Bulavin
la source
1

Utilisez cette extension, au cas où vous ne deviez redimensionner la largeur / hauteur qu'avec le rapport hauteur / largeur.

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))
    }
}
Jeffrey Neo
la source