UIImage: Zmień rozmiar, a następnie Przytnij

187

Walę się w tę twarz dosłownie od kilku dni i chociaż ciągle czuję, że jestem na skraju objawienia, po prostu nie mogę osiągnąć celu.

Pomyślałem z wyprzedzeniem w fazach koncepcyjnych mojego projektu, że trywialne byłoby uchwycenie obrazu z aparatu lub biblioteki iPhone'a, przeskalowanie go do określonej wysokości, przy użyciu funkcji równoważnej opcji wypełniania proporcji UIImageView (w całości w kodzie), a następnie przyciąć wyłączyć wszystko, co nie pasuje w upłynął CGRect.

Uzyskanie oryginalnego obrazu z kamery lub biblioteki było banalne. Jestem zszokowany, jak trudne okazały się pozostałe dwa kroki.

Załączony obraz pokazuje, co próbuję osiągnąć. Czy ktoś mógłby być na tyle miły, by trzymać mnie za rękę? Każdy przykład kodu, który do tej pory znalazłem, wydaje się rozbijać obraz, być do góry nogami, wyglądać jak bzdury, rysować poza granicami lub w inny sposób po prostu nie działać poprawnie.

Carson C.
źródło
7
Link jest zepsuty dla twojego obrazu.
1
Jedno, co nas zaskoczyło, to dlaczego Apple nie dodało go do UIImagePickerController - jest to zbyt cholernie trudne ;-)
Tim

Odpowiedzi:

246

Potrzebowałem tego samego - w moim przypadku, aby wybrać wymiar, który pasuje po skalowaniu, a następnie przyciąć każdy koniec, aby dopasować resztę do szerokości. (Pracuję w układzie poziomym, więc mogłem nie zauważyć żadnych niedociągnięć w trybie portretowym.) Oto mój kod - jest częścią strategii na UIImage. Rozmiar docelowy w moim kodzie jest zawsze ustawiony na pełny ekran urządzenia.

@implementation UIImage (Extras)

#pragma mark -
#pragma mark Scale and crop image

- (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize
{
    UIImage *sourceImage = self;
    UIImage *newImage = nil;    
    CGSize imageSize = sourceImage.size;
    CGFloat width = imageSize.width;
    CGFloat height = imageSize.height;
    CGFloat targetWidth = targetSize.width;
    CGFloat targetHeight = targetSize.height;
    CGFloat scaleFactor = 0.0;
    CGFloat scaledWidth = targetWidth;
    CGFloat scaledHeight = targetHeight;
    CGPoint thumbnailPoint = CGPointMake(0.0,0.0);

    if (CGSizeEqualToSize(imageSize, targetSize) == NO) 
    {
        CGFloat widthFactor = targetWidth / width;
        CGFloat heightFactor = targetHeight / height;

        if (widthFactor > heightFactor) 
        {
            scaleFactor = widthFactor; // scale to fit height
        }
        else
        {
            scaleFactor = heightFactor; // scale to fit width
        }

        scaledWidth  = width * scaleFactor;
        scaledHeight = height * scaleFactor;

        // center the image
        if (widthFactor > heightFactor)
        {
            thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5; 
        }
        else
        {
            if (widthFactor < heightFactor)
            {
                thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
            }
        }
    }   

    UIGraphicsBeginImageContext(targetSize); // this will crop

    CGRect thumbnailRect = CGRectZero;
    thumbnailRect.origin = thumbnailPoint;
    thumbnailRect.size.width  = scaledWidth;
    thumbnailRect.size.height = scaledHeight;

    [sourceImage drawInRect:thumbnailRect];

    newImage = UIGraphicsGetImageFromCurrentImageContext();

    if(newImage == nil)
    {
        NSLog(@"could not scale image");
    }

    //pop the context to get back to the default
    UIGraphicsEndImageContext();

    return newImage;
}
Jane Sales
źródło
1
co ciekawe, działa w symulatorze, ale na urządzeniu otrzymuję ExecBadAccess ..
Sorin Antohi
5
Występuje problem ze złym exec, ponieważ funkcje UIImage nie są bezpieczne dla wątków. Właśnie dlatego czasami się zawiesza, a czasem nie
Erik,
1
Ten kod działał dla mnie świetnie, ale na siatkówce był niewyraźny. Połączenie tego kodu z poniższym komentarzem sprawiło, że wszystko stało się idealne: stackoverflow.com/questions/603907/…
MaxGabriel,
23
Użyj UIGraphicsBeginImageContextWithOptions (targetSize, YES, 0.0); aby obraz był również miły na siatkówce.
Borut Tomazin
1
Działa to dla mnie w trybie portretowym, ale nie w poziomie. Wysokość jest rozciągnięta w krajobraz. Masz pomysł, dlaczego?
Darren,
77

Starszy post zawiera kod metody zmiany rozmiaru twojego UIImage. Odpowiednia część jest następująca:

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

   return newImage;
}

Jeśli chodzi o kadrowanie, uważam, że jeśli zmienisz metodę, aby użyć innego rozmiaru dla skalowania niż dla kontekstu, wynikowy obraz powinien zostać przycięty do granic kontekstu.

Brad Larson
źródło
3
Nie ma sensu, dlaczego to naprawia orientację obrazu, ale tak robi, a tym samym rozwiązuje mój problem z tym, że aparat nie zwraca prawidłowej orientacji w originalImage. Dzięki.
Brenden,
10
Odkryłem, że zmiana rozmiaru obrazu na urządzeniu z siatkówką była niewyraźna. Aby zachować przejrzystość, ja zmodyfikowany pierwszy wiersz na następujący: UIGraphicsBeginImageContextWithOptions(newSize, 1.0f, 0.0f);. (wyjaśniono tutaj: stackoverflow.com/questions/4334233/... )
johngraham
Obraca obraz - ale nie przycina go poprawnie! Dlaczego ma tyle głosów?
Dejell
18
+ (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)targetSize {
    //If scaleFactor is not touched, no scaling will occur      
    CGFloat scaleFactor = 1.0;

    //Deciding which factor to use to scale the image (factor = targetSize / imageSize)
    if (image.size.width > targetSize.width || image.size.height > targetSize.height)
        if (!((scaleFactor = (targetSize.width / image.size.width)) > (targetSize.height / image.size.height))) //scale to fit width, or
            scaleFactor = targetSize.height / image.size.height; // scale to fit heigth.

    UIGraphicsBeginImageContext(targetSize); 

    //Creating the rect where the scaled image is drawn in
    CGRect rect = CGRectMake((targetSize.width - image.size.width * scaleFactor) / 2,
                             (targetSize.height -  image.size.height * scaleFactor) / 2,
                             image.size.width * scaleFactor, image.size.height * scaleFactor);

    //Draw the image into the rect
    [image drawInRect:rect];

    //Saving the image, ending image context
    UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return scaledImage;
}

Proponuję ten. Czy ona nie jest pięknością? ;)

Ktoś
źródło
1
Ten jest dobry, jeśli najpierw usuniesz instrukcję if, która działa jak AspectFill.
RaffAl
7

Proszę bardzo. Ten jest idealny ;-)

EDYCJA: patrz poniżej komentarz - „Nie działa z niektórymi obrazami, kończy się niepowodzeniem z: CGContextSetInterpolationQuality: nieprawidłowy kontekst 0x0 błąd”

// Resizes the image according to the given content mode, taking into account the image's orientation
- (UIImage *)resizedImageWithContentMode:(UIViewContentMode)contentMode imageToScale:(UIImage*)imageToScale bounds:(CGSize)bounds interpolationQuality:(CGInterpolationQuality)quality {
    //Get the size we want to scale it to
    CGFloat horizontalRatio = bounds.width / imageToScale.size.width;
    CGFloat verticalRatio = bounds.height / imageToScale.size.height;
    CGFloat ratio;

    switch (contentMode) {
        case UIViewContentModeScaleAspectFill:
            ratio = MAX(horizontalRatio, verticalRatio);
            break;

        case UIViewContentModeScaleAspectFit:
            ratio = MIN(horizontalRatio, verticalRatio);
            break;

        default:
            [NSException raise:NSInvalidArgumentException format:@"Unsupported content mode: %d", contentMode];
    }

    //...and here it is
    CGSize newSize = CGSizeMake(imageToScale.size.width * ratio, imageToScale.size.height * ratio);


    //start scaling it
    CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
    CGImageRef imageRef = imageToScale.CGImage;
    CGContextRef bitmap = CGBitmapContextCreate(NULL,
                                                newRect.size.width,
                                                newRect.size.height,
                                                CGImageGetBitsPerComponent(imageRef),
                                                0,
                                                CGImageGetColorSpace(imageRef),
                                                CGImageGetBitmapInfo(imageRef));

    CGContextSetInterpolationQuality(bitmap, quality);

    // Draw into the context; this scales the image
    CGContextDrawImage(bitmap, newRect, imageRef);

    // Get the resized image from the context and a UIImage
    CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap);
    UIImage *newImage = [UIImage imageWithCGImage:newImageRef];

    // Clean up
    CGContextRelease(bitmap);
    CGImageRelease(newImageRef);

    return newImage;
}
cdstamper
źródło
1
Wyglądają naprawdę pięknie :) Podoba mi się tutaj InterpolationQuality
Kostiantyn Sokolinskyi
Nie działa z niektórymi obrazami, kończy się niepowodzeniem z: CGContextSetInterpolationQuality: nieprawidłowy kontekst Błąd 0x0
RaffAl
6

To jest wersja odpowiedzi Jane Sales w Swift. Twoje zdrowie!

public func resizeImage(image: UIImage, size: CGSize) -> UIImage? {
    var returnImage: UIImage?

    var scaleFactor: CGFloat = 1.0
    var scaledWidth = size.width
    var scaledHeight = size.height
    var thumbnailPoint = CGPointMake(0, 0)

    if !CGSizeEqualToSize(image.size, size) {
        let widthFactor = size.width / image.size.width
        let heightFactor = size.height / image.size.height

        if widthFactor > heightFactor {
            scaleFactor = widthFactor
        } else {
            scaleFactor = heightFactor
        }

        scaledWidth = image.size.width * scaleFactor
        scaledHeight = image.size.height * scaleFactor

        if widthFactor > heightFactor {
            thumbnailPoint.y = (size.height - scaledHeight) * 0.5
        } else if widthFactor < heightFactor {
            thumbnailPoint.x = (size.width - scaledWidth) * 0.5
        }
    }

    UIGraphicsBeginImageContextWithOptions(size, true, 0)

    var thumbnailRect = CGRectZero
    thumbnailRect.origin = thumbnailPoint
    thumbnailRect.size.width = scaledWidth
    thumbnailRect.size.height = scaledHeight

    image.drawInRect(thumbnailRect)
    returnImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()

    return returnImage
}
Ishan Handa
źródło
2

Zmodyfikowałem Kod Brada Larsona. Będzie wypełniał obraz w danym prostokącie.

-(UIImage*) scaleAndCropToSize:(CGSize)newSize;
{
    float ratio = self.size.width / self.size.height;

    UIGraphicsBeginImageContext(newSize);

    if (ratio > 1) {
        CGFloat newWidth = ratio * newSize.width;
        CGFloat newHeight = newSize.height;
        CGFloat leftMargin = (newWidth - newHeight) / 2;
        [self drawInRect:CGRectMake(-leftMargin, 0, newWidth, newHeight)];
    }
    else {
        CGFloat newWidth = newSize.width;
        CGFloat newHeight = newSize.height / ratio;
        CGFloat topMargin = (newHeight - newWidth) / 2;
        [self drawInRect:CGRectMake(0, -topMargin, newSize.width, newSize.height/ratio)];
    }

    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}
Seunghoon
źródło
1
Testowany z dwoma innymi obrazami, portretowym i krajobrazowym. To NIE wykonuje wypełniania aspektu.
RaffAl,
2

Wersja Xamarin.iOS dla akceptowanej odpowiedzi dotyczącej zmiany rozmiaru, a następnie przycięcia obrazu UIImage (wypełnienie aspektu) znajduje się poniżej

    public static UIImage ScaleAndCropImage(UIImage sourceImage, SizeF targetSize)
    {
        var imageSize = sourceImage.Size;
        UIImage newImage = null;
        var width = imageSize.Width;
        var height = imageSize.Height;
        var targetWidth = targetSize.Width;
        var targetHeight = targetSize.Height;
        var scaleFactor = 0.0f;
        var scaledWidth = targetWidth;
        var scaledHeight = targetHeight;
        var thumbnailPoint = PointF.Empty;
        if (imageSize != targetSize)
        {
            var widthFactor = targetWidth / width;
            var heightFactor = targetHeight / height;
            if (widthFactor > heightFactor)
            {
                scaleFactor = widthFactor;// scale to fit height
            }
            else
            {
                scaleFactor = heightFactor;// scale to fit width
            }
            scaledWidth = width * scaleFactor;
            scaledHeight = height * scaleFactor;
            // center the image
            if (widthFactor > heightFactor)
            {
                thumbnailPoint.Y = (targetHeight - scaledHeight) * 0.5f;
            }
            else
            {
                if (widthFactor < heightFactor)
                {
                    thumbnailPoint.X = (targetWidth - scaledWidth) * 0.5f;
                }
            }
        }
        UIGraphics.BeginImageContextWithOptions(targetSize, false, 0.0f);
        var thumbnailRect = new RectangleF(thumbnailPoint, new SizeF(scaledWidth, scaledHeight));
        sourceImage.Draw(thumbnailRect);
        newImage = UIGraphics.GetImageFromCurrentImageContext();
        if (newImage == null)
        {
            Console.WriteLine("could not scale image");
        }
        //pop the context to get back to the default
        UIGraphics.EndImageContext();

        return newImage;
    }
Alex Sorokoletov
źródło
2

Przekształciłem przewodnik Sama Wircha w szybki i zadziałało to dla mnie dobrze, chociaż na końcowym obrazie jest pewne bardzo „zgniecenie”, którego nie mogłem rozwiązać.

func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage {
    var ratio: CGFloat = 0
    var delta: CGFloat = 0
    var offset = CGPointZero
    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)
    }
    let clipRect = CGRectMake(-offset.x, -offset.y, (ratio * image.size.width) + delta, (ratio * image.size.height) + delta)
    UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0)
    UIRectClip(clipRect)
    image.drawInRect(clipRect)
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage
}

Jeśli ktoś chce wersję C celu, jest na swojej stronie internetowej.

William T.
źródło
2

Odkryłem, że Swift 3 opublikowany przez Evgenii Kanvets nie skaluje obrazu w jednolity sposób.

Oto moja wersja Swift 4 funkcji, która nie psuje obrazu:

static func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage? {

    // This function returns a newImage, based on image
    // - image is scaled uniformaly to fit into a rect of size newSize
    // - if the newSize rect is of a different aspect ratio from the source image
    //     the new image is cropped to be in the center of the source image
    //     (the excess source image is removed)

    var ratio: CGFloat = 0
    var delta: CGFloat = 0
    var drawRect = CGRect()

    if newSize.width > newSize.height {

        ratio = newSize.width / image.size.width
        delta = (ratio * image.size.height) - newSize.height
        drawRect = CGRect(x: 0, y: -delta / 2, width: newSize.width, height: newSize.height + delta)

    } else {

        ratio = newSize.height / image.size.height
        delta = (ratio * image.size.width) - newSize.width
        drawRect = CGRect(x: -delta / 2, y: 0, width: newSize.width + delta, height: newSize.height)

    }

    UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0)
    image.draw(in: drawRect)
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage
} 
Chris Capener
źródło
Musiałem ustawić skalowanie na 1,0 w, UIGraphicsBeginImageContextWithOptionsale inne działały idealnie. Dzięki!
maxmzd
1

Poniższy prosty kod działał dla mnie.

[imageView setContentMode:UIViewContentModeScaleAspectFill];
[imageView setClipsToBounds:YES];
William Li
źródło
Twój kod działa dobrze dla UIImageViewobiektów. To pytanie dotyczy jednak skalowania samego UIImageobiektu.
Rickster,
1
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0,0.0,ScreenWidth,ScreenHeigth)];
    [scrollView setBackgroundColor:[UIColor blackColor]];
    [scrollView setDelegate:self];
    [scrollView setShowsHorizontalScrollIndicator:NO];
    [scrollView setShowsVerticalScrollIndicator:NO];
    [scrollView setMaximumZoomScale:2.0];
    image=[image scaleToSize:CGSizeMake(ScreenWidth, ScreenHeigth)];
    imageView = [[UIImageView alloc] initWithImage:image];
    UIImageView* imageViewBk = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background.png"]];
    [self.view addSubview:imageViewBk];
    CGRect rect;
    rect.origin.x=0;
    rect.origin.y=0;
    rect.size.width = image.size.width;
    rect.size.height = image.size.height;

    [imageView setFrame:rect];

    [scrollView setContentSize:[imageView frame].size];
    [scrollView setMinimumZoomScale:[scrollView frame].size.width / [imageView frame].size.width];
    [scrollView setZoomScale:[scrollView minimumZoomScale]];
    [scrollView addSubview:imageView];

    [[self view] addSubview:scrollView];

dzięki temu możesz wykonać zrzuty ekranu na swoje zdjęcie

float zoomScale = 1.0 / [scrollView zoomScale];
CGRect rect;
rect.origin.x = [scrollView contentOffset].x * zoomScale;
rect.origin.y = [scrollView contentOffset].y * zoomScale;
rect.size.width = [scrollView bounds].size.width * zoomScale;
rect.size.height = [scrollView bounds].size.height * zoomScale;

CGImageRef cr = CGImageCreateWithImageInRect([[imageView image] CGImage], rect);

UIImage *cropped = [UIImage imageWithCGImage:cr];

CGImageRelease(cr);
khaled
źródło
0
- (UIImage*)imageScale:(CGFloat)scaleFactor cropForSize:(CGSize)targetSize
{
    targetSize = !targetSize.width?self.size:targetSize;
    UIGraphicsBeginImageContext(targetSize); // this will crop

    CGRect thumbnailRect = CGRectZero;

    thumbnailRect.size.width  = targetSize.width*scaleFactor;
    thumbnailRect.size.height = targetSize.height*scaleFactor;
    CGFloat xOffset = (targetSize.width- thumbnailRect.size.width)/2;
    CGFloat yOffset = (targetSize.height- thumbnailRect.size.height)/2;
    thumbnailRect.origin = CGPointMake(xOffset,yOffset);

    [self drawInRect:thumbnailRect];

    UIImage *newImage  = UIGraphicsGetImageFromCurrentImageContext();

    if(newImage == nil)
    {
        NSLog(@"could not scale image");
    }

    UIGraphicsEndImageContext();

    return newImage;
}

Poniżej przykład pracy: Lewy obraz - (obraz źródłowy); Właściwy obraz ze skalą x2

wprowadź opis zdjęcia tutaj

Jeśli chcesz skalować obraz, ale zachować jego ramkę (proporcje), wywołaj metodę w ten sposób:

[yourImage imageScale:2.0f cropForSize:CGSizeZero];
David
źródło
0

Wydaje się, że to pytanie zostało uspokojone, ale w moim poszukiwaniu rozwiązania, które łatwiej byłoby zrozumieć (i napisane w Swift), doszedłem do tego (napisałem również: Jak przyciąć magię UIImage? )


Chciałem móc kadrować z regionu opartego na współczynniku kształtu i skalować do rozmiaru opartego na zewnętrznym zasięgu. Oto moja odmiana:

import AVFoundation
import ImageIO

class Image {

    class func crop(image:UIImage, crop source:CGRect, aspect:CGSize, outputExtent:CGSize) -> UIImage {

        let sourceRect = AVMakeRectWithAspectRatioInsideRect(aspect, source)
        let targetRect = AVMakeRectWithAspectRatioInsideRect(aspect, CGRect(origin: CGPointZero, size: outputExtent))

        let opaque = true, deviceScale:CGFloat = 0.0 // use scale of device's main screen
        UIGraphicsBeginImageContextWithOptions(targetRect.size, opaque, deviceScale)

        let scale = max(
            targetRect.size.width / sourceRect.size.width,
            targetRect.size.height / sourceRect.size.height)

        let drawRect = CGRect(origin: -sourceRect.origin * scale, size: image.size * scale)
        image.drawInRect(drawRect)

        let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return scaledImage
    }
}

Jest kilka rzeczy, które uznałem za mylące, oddzielne obawy dotyczące kadrowania i zmiany rozmiaru. Kadrowanie jest obsługiwane przez początek prostokąta, który przekazujesz do drawInRect, a skalowanie jest obsługiwane przez część rozmiaru. W moim przypadku musiałem powiązać rozmiar prostownika przycinania na źródle z moim odbiciem wyjściowym o tym samym współczynniku kształtu. Współczynnik skali jest następnie wyprowadzany / wprowadzany i należy to zastosować do drawRect (przekazanego do drawInRect).

Jednym zastrzeżeniem jest to, że takie podejście skutecznie zakłada, że ​​rysowany obraz jest większy niż kontekst obrazu. Nie testowałem tego, ale myślę, że możesz użyć tego kodu do obsługi kadrowania / powiększania, ale jawnie definiując parametr skali jako wyżej wspomniany parametr skali. Domyślnie UIKit stosuje mnożnik oparty na rozdzielczości ekranu.

Na koniec należy zauważyć, że to podejście UIKit jest wyższe niż podejście CoreGraphics / Quartz i Core Image i wydaje się, że rozwiązuje problemy z orientacją obrazu. Warto również wspomnieć, że jest dość szybki, po ImageIO, zgodnie z tym postem tutaj: http://nshipster.com/image-resizing/

Chris Conover
źródło
0

Wersja szybka:

    static func imageWithImage(image:UIImage, newSize:CGSize) ->UIImage {
    UIGraphicsBeginImageContextWithOptions(newSize, true, UIScreen.mainScreen().scale);
    image.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height))

    let newImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();
    return newImage
}
Roman Barzyczak
źródło
1
Fajna mała funkcja, ale jestem pewien, że to po prostu rozciąga obraz do określonego rozmiaru, nie utrzymuje proporcji i przycięcia ...
William T.
Tak, masz rację, dotyczy to tylko przycinania obrazu do drobnego rozmiaru.
Roman Barzyczak,
0

Oto wersja Swift 3 przewodnika Sama Wircha po swift opublikowanego przez Williama T.

extension UIImage {

    static func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage? {
        var ratio: CGFloat = 0
        var delta: CGFloat = 0
        var offset = CGPoint.zero

        if image.size.width > image.size.height {
            ratio = newSize.width / image.size.width
            delta = (ratio * image.size.width) - (ratio * image.size.height)
            offset = CGPoint(x: delta / 2, y: 0)
        } else {
            ratio = newSize.width / image.size.height
            delta = (ratio * image.size.height) - (ratio * image.size.width)
            offset = CGPoint(x: 0, y: delta / 2)
        }

        let clipRect = CGRect(x: -offset.x, y: -offset.y, width: (ratio * image.size.width) + delta, height: (ratio * image.size.height) + delta)
        UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0)
        UIRectClip(clipRect)
        image.draw(in: clipRect)
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage
    }

}
Jewhenii Kanivets
źródło