Najprostszy sposób zmiany rozmiaru UIImage?

397

W mojej aplikacji na iPhone robię zdjęcie aparatem, a następnie chcę zmienić jego rozmiar na 290 * 390 pikseli. Użyłem tej metody do zmiany rozmiaru obrazu:

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

Działa idealnie, ale jest to funkcja nieudokumentowana, więc nie mogę jej używać z iPhone'em OS4.

Więc ... jaki jest najprostszy sposób zmiany rozmiaru Maga UII?

pimpampoum
źródło
Sposób na zrobienie tego w 2019 r., Nshipster.com/image-resizing
dengST30

Odpowiedzi:

772

Najprostszym sposobem jest ustawienie ramki UIImageViewi ustawienie contentModejednej z opcji zmiany rozmiaru.

Lub możesz użyć tej metody narzędzia, jeśli faktycznie chcesz zmienić rozmiar obrazu:

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

Przykładowe użycie:

#import "MYUtil.h"

UIImage *myIcon = [MYUtil imageWithImage:myUIImageInstance scaledToSize:CGSizeMake(20, 20)];
Paul Lynch
źródło
11
NIE zatrzymałbym tutaj wyniku. newImagezostanie już automatycznie wydany i powinno to zależeć od metody, która to wywołała, aby go zachować lub nie. To podejście jest po prostu błaganiem o błąd pamięci, ponieważ initnie jest częścią nazwy metody. Spodziewałbym się, że metoda o takiej nazwie zwróci automatycznie wydany obiekt.
Alex Wayne
Ważny punkt i edytowany. Po prostu zostawię ostrzeżenie, że mój kod, który tego używa, generuje błędy malloc gdzie indziej, bez dodatkowego zachowania, co jest oczywiście moja wina :-)
Paul Lynch
26
Począwszy od iOS 4.0 , wszystkie funkcje UIGraphics * są bezpieczne dla wątków.
BJ Homer
3
@Paul Lynch: Pamiętaj, że ta zmiana przerywa misję oryginalnego postu: Jak zmienić rozmiar obrazu do pewnego dokładnego rozmiaru w pikselach (w przeciwieństwie do rozmiaru punktowego ).
Nikolai Ruhe
8
Dla tych, którzy walczą z powodu wskazanego ograniczenia @NikolaiRuhe, możesz zmusić go do uzyskania dokładnego rozmiaru w pikselach, przekazując 1,0 zamiast 0,0 w wezwaniu doUIGraphicsBeginImageContextWithOptions
Danny
84

Oto szybka wersja odpowiedzi Paula Lyncha

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
}

I jako rozszerzenie:

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
źródło
2
właśnie zauważyłem, że w iOS Twój nowy rozmiar będzie mnożony przez 2x, 3x w zależności od urządzenia
Sruit A.Suk
6
UIGraphicsBeginImageContextWithOptions (newSize, false, image.scale); dla wszystkich urządzeń
phnmnn
Pamiętaj, że nie zmienia to rozmiaru bazowego CGImage (przynajmniej w iOS 12.1). Co jest w porządku na podstawie pytania, ale jeśli zapisujesz obraz, będziesz musiał użyć cgImage, a otrzymasz oryginalny obraz.
prewett
72

Rozwiązanie Proper Swift 3.0 dla iOS 10+ : ImageRendererSkładnia przy użyciu i zamykaniu:

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

A oto wersja 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
źródło
1
Bardzo eleganckie rozwiązanie, to powinna być zaakceptowana odpowiedź!
appsunited
2
To jest lepsze niż eleganckie, to prawda. Dokumenty API firmy Apple zalecają, aby nowy kod korzystał z UIGraphicsRenderer tak jak ten kod. Dziękuję
Paul Bruneau,
1
@appsunited Pytanie ma prawie 8 lat. Ta metoda działa tylko na iOS10 +, jak stwierdzono.
Podczas kopiowania nowego obiektu obrazu do schowka tworzy dla mnie biały blok. W rzeczywistości nie zmienia rozmiaru widoku obrazu?
Oliver Dixon,
1
ta funkcja zwraca obraz o wysokości 0 i szerokości 0, spodziewana wysokość to 9000 wysokość i 9000 szerokość.
krishan kumar
31

Trevor Howard ma kilka kategorii UIImage które całkiem ładnie radzą sobie ze zmianą rozmiaru. Jeśli nic więcej, możesz użyć kodu jako przykładów.

Uwaga: od wersji iOS 5.1 ta odpowiedź może być nieprawidłowa. Zobacz komentarz poniżej.

TechZen
źródło
2
Musiałem też zmienić rozmiar niektórych zdjęć i najpierw wypróbowałem dodatki Trevora do UIImage, ale dostałem kilka dziwnych błędów w PNG (coś o kanale alfa). Przyjęta odpowiedź na to pytanie działała jednak dobrze.
Sorig,
1
To już nie jest ważne (przynajmniej w iOS5.1
Jann
@Jann, spójrz na commits pod postem.
vikingosegundo
@Jann Jeśli zastosujesz się do poprawek sugerowanych w komentarzach, przejdź do „Matt mówi: 22 listopada 2011 o 17:39”, ponieważ wypuszcza colorspaceref (w przeciwieństwie do podobnego rozwiązania w podkowy 7).
Ben Flynn
1
@sanmai przy użyciu dowolnej z tych metod zmiany rozmiaru obrazów traci ostrość. czy jest jakiś sposób na osiągnięcie ostrości
vamsi575kg
31

Bardziej kompaktowa wersja dla Swift 4 i iOS 10+:

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

Stosowanie:

let resizedImage = image.resized(to: CGSize(width: 50, height: 50))
neave
źródło
26

Widziałem też, jak to zrobiono (którego używam UIButtonsdla stanu normalnego i wybranego, ponieważ przyciski tego nie robią)resize pasują). Kredyty należą się każdemu, kto był oryginalnym autorem.

Najpierw utwórz pusty plik .h i .m o nazwie UIImageResizing.hiUIImageResizing.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

Uwzględnij ten plik .h w dowolnym pliku .m, w którym będziesz używać funkcji, a następnie wywołaj go w następujący sposób:

UIImage* image = [UIImage imageNamed:@"largeImage.png"];
UIImage* smallImage = [image scaleToSize:CGSizeMake(100.0f,100.0f)];
okradziono mnie
źródło
Nie kopiuje to orientacji obrazu.
Kevin,
20

To ulepszenie kodu Paula zapewni ostry obraz wysokiej rozdzielczości na iPhonie z wyświetlaczem Retina. W przeciwnym razie podczas zmniejszania jest rozmazany.

+ (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
źródło
13
Po prostu FYI, zmiany, które wprowadziłeś, są niepotrzebne, ponieważ podanie wartości 0,0 dla skali UIGraphicsBeginImageContextWithOptionsspowoduje automatyczne użycie skali głównego ekranu (od iOS 3.2).
Andrew R.,
16

Oto prosty sposób:

    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 to nowy obraz.

asmad
źródło
16

Szybkie rozwiązanie dla Stretch Fill, Aspect Fill i 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()
    }
}

i do użycia możesz wykonać następujące czynności:

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

Dzięki abdullahselek za jego oryginalne rozwiązanie.

Josh Bernfeld
źródło
15

Oto modyfikacja kategorii napisana przez iWasRobbed powyżej. Zachowuje proporcje oryginalnego obrazu zamiast go zniekształcać.

- (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 Means
źródło
11

Jeśli chcesz tylko powiększyć obraz i nie zależy ci na dokładnym rozmiarze:

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

Ustawienie skali na 0.25fdaje obraz 816 na 612 z kamery 8MP.

Oto kategoria UIImage + Skala dla tych, którzy jej potrzebują.

sanmai
źródło
10

Dlaczego tak skomplikowane? Myślę, że użycie systemowego interfejsu API może osiągnąć ten sam wynik:

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.

Jedynym problemem jest to, że jako drugi argument należy przekazać odwrotność współczynnika docelowego, ponieważ zgodnie z dokumentem drugi parametr określa stosunek oryginalnego obrazu w porównaniu do nowego skalowanego.

Chris
źródło
Ponieważ skala (%) to nie to samo, co piksele (w / h).
Skala powinna być duża. Obraz / skala / stosunek nie 1 / stosunek.
Marián Černý
9

Jest to rozszerzenie UIImage kompatybilne z Swift 3 i Swift 4, które skaluje obraz do danego rozmiaru o proporcjach

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

}

Przykładowe użycie

let image = UIImage(named: "apple")
let scaledImage = image.scaleImageToFitSize(size: CGSize(width: 45.0, height: 45.0))
abdullahselek
źródło
Poleciłbym nazwać funkcję scaledImage(with size:)lub scaledWithSize(_:). Też UIGraphicsGetImageFromCurrentImageContextnie może tak naprawdę wrócić nil, więc też !by działało. Możesz także uprościć tworzenie poprawek do CGRect(origin: .zero, size: size).
Sulthan
8

Znalazłem kategorię dla UIImage na własnych przykładach Apple'a, która robi tę samą sztuczkę. Oto link: https://developer.apple.com/library/ios/samplecode/sc2273/Listings/AirDropSample_UIImage_Resize_m.html .

Musisz tylko zmienić połączenie:

UIGraphicsBeginImageContextWithOptions(newSize, YES, 2.0);

w imageWithImage:scaledToSize:inRect:z:

UIGraphicsBeginImageContextWithOptions(newSize, NO, 2.0);

Aby uwzględnić kanał alfa na obrazie.

aubykhan
źródło
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
źródło
Dobrym pomysłem jest dodanie tekstu opisowego do kodu.
GMchris
6

Dla moich kolegów Xamarians, tutaj jest Xamarin.iOS C # wersja @Paul Lynch odpowiedź.

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
źródło
5

Jeśli chcesz utworzyć miniaturę obrazu UIImage (z proporcjonalnym rozmiarem lub być może z pewnym przycinaniem), sprawdź kategorię UIImage + Resize, która pozwala używać zwięzłej składni podobnej do ImageMagick:

UIImage* squareImage       = [image resizedImageByMagick: @"320x320#"];
Vlad Andersen
źródło
5

[cf Chris] Aby zmienić rozmiar do pożądanego rozmiaru:

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

lub równoważnie zastąpić CGImageGetWidth(...)/DESIREDWIDTH

Tiritea
źródło
Należy pamiętać, że same dane obrazu nie są zmieniane, ale stosowany jest współczynnik skali, co oznacza, że ​​obraz ma ten sam rozmiar w pamięci.
inkredibl
5

Rogerio Chaves odpowiada jako szybkie rozszerzenie

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
}

A także bonus

func scaledTo(height: CGFloat) -> UIImage{
    let width = height*self.size.width/self.size.height
    return scaledTo(size: CGSize(width: width, height: height))
}
Intencje
źródło
5

Swift 3.0 z opcją bezpieczeństwa (w przypadku błędu zwraca oryginalny obraz):

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
}
zakładać zawłokę
źródło
5

(Kompatybilny z Swift 4) iOS 10+ i iOS <10 (używając, UIGraphicsImageRendererjeśli to możliwe, w UIGraphicsGetImageFromCurrentImageContextprzeciwnym razie)

/// 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!
    }
}
Natan
źródło
5

Skuteczne podejście bez rozciągania obrazu 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!
                }

// Metoda wywołania

    let resizedImage = self.resize(image: UIImage(named: "YourImageName")!, toScaleSize: CGSize(width: 290, height: 390))
Zespół iOS
źródło
3

Odpowiedź Paula Lyncha jest świetna, ale zmieniłaby proporcje obrazu. jeśli nie chcesz zmieniać proporcji obrazu i nadal chcesz, aby nowy obraz pasował do nowego rozmiaru, spróbuj tego.

+ (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
źródło
3

użyj tego rozszerzenia

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
źródło
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
źródło
2

Oto mój nieco pełny kod Swift

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
źródło
1
To oszczędzało mi czas. Dzięki!
Quy Nguyen,
przydatne, ale zachowuj się dziwnie po segregacji i powracających czynnościach, zobacz tutaj plz: stackoverflow.com/questions/30978685/...
He Yifei
Uwaga: Możesz dodać warunek wstępny ( guard) do aspectRatioAwareSizefunkcji, aby nie generował wyjątku dzielenia przez zero, jeśli parametry wejściowe dla imageSize lub boxSize toCGSize.zero
pkluz
@ pkluz jest to dobra sugestia, ale myślę, że będziesz potrzebować ich w całej funkcji, ponieważ każda zmienna jest dzielnikiem innej linii ... Zostawię to tak, jak dla przejrzystości. W przypadku produkcji lub biblioteki github, możesz chcieć to pogłębić ... Bardziej niż strażnikiem, martwię się, że funkcja używa krótkich nazw zmiennych, które niewiele mówią. Najpierw prawdopodobnie to naprawię, a potem dodam osłony dla innych rzeczy, a następnie dodam testy jednostkowe.
Dan Rosenstark,
2

Odpowiedź 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
źródło
Nie ma potrzeby ustawiania rzeczywistej skali: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

Odkryłem, że trudno jest znaleźć odpowiedź, której można użyć natychmiast po wyjęciu z pudełka w projekcie Swift 3 . Głównym problemem innych odpowiedzi jest to, że nie honorują one kanału alfa obrazu. Oto technika, której używam w swoich projektach.

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

Przykład użycia:

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

Ten kod jest przepisanym rozszerzeniem Apple z dodaną obsługą obrazów z kanałem alfa i bez niego.

Jako dalsze czytanie polecam sprawdzenie tego artykułu pod kątem różnych technik zmiany rozmiaru obrazu. Obecne podejście oferuje przyzwoitą wydajność, obsługuje interfejsy API wysokiego poziomu i jest łatwe do zrozumienia. Zalecam trzymanie się tego, chyba że zmiana rozmiaru obrazu stanowi wąskie gardło w wydajności.

Vadim Bulavin
źródło
1

Skorzystaj z tego rozszerzenia, jeśli chcesz zmienić szerokość / wysokość tylko z proporcjami obrazu.

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
źródło