89

I have been struggling resizing an image. Basically I have stumpled upon: How to scale down a UIImage and make it crispy / sharp at the same time instead of blurry?

This seems to be a legit solution but somehow it is not working correctly.

My app works with Photos from the Camera Roll. This photos should be resized to about 200x200 whereas the width is important, not the height.

Unfortunetly I am not having a sample code as I discarded it in my rage about non working solution, sorry.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
swift_dan
  • 2,628
  • 2
  • 12
  • 19
  • What exactly not working correctly? What result you expect and what result did you get? – ifau Aug 12 '15 at 15:40
  • i expect an image with a width of 200 and in this ratio a height of x. I am still getting the original image like 1920x.... whatever – swift_dan Aug 13 '15 at 09:06

13 Answers13

156

Here is my code. The Image is in width 850 px and not 200 px:

 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
}


@IBAction func chooseImage(sender: AnyObject) {


    var myPickerController = UIImagePickerController()
    myPickerController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
    myPickerController.delegate = self;
    self.presentViewController(myPickerController, animated: true, completion: nil)


}

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject])

{
    var imagenow = info[UIImagePickerControllerOriginalImage] as? UIImage

    imageImage.image = resizeImage(imagenow!, newWidth: 200)



    pimg2 = imageImage.image!

    cidnew2 = textFieldCID!.text!
    pname2 = textFieldName!.text
    pmanu2 = textFieldMan!.text
    pnick2 = textFieldNick!.text
    podate2 = textFieldPODate!.text
    pno2 = textFieldArtNo!.text



    self.dismissViewControllerAnimated(true, completion: nil)

}
Ivan Yurchenko
  • 3,762
  • 1
  • 21
  • 35
swift_dan
  • 2,628
  • 2
  • 12
  • 19
  • 1
    to get rid off the errors in swift 3, information provided in the http://stackoverflow.com/a/37948158/1404324 could help – Faruk Dec 05 '16 at 10:34
  • 5
    working in swift 4: `func resizeImage(image: UIImage, newWidth: CGFloat) -> UIImage { let scale = newWidth / image.size.width let newHeight = image.size.height * scale UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight)) image.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight)) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! }` – mikey9 Aug 30 '18 at 03:37
68

Based on swift_dan's answer, an update for Swift 3

func resizeImage(image: UIImage, newWidth: CGFloat) -> UIImage? {

    let scale = newWidth / image.size.width
    let newHeight = image.size.height * scale
    UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
    image.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))

    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage
}
Christoph R
  • 928
  • 6
  • 10
  • 2
    If you want save quality in retina displays and save transparent, use: UIGraphicsBeginImageContextWithOptions(CGSize(width: newWidth, height: newHeight), false, 0) – Evgeny Karev Aug 01 '17 at 22:15
  • This Reduced my image from 18MB to < than 1 MB! I used: resizeImage(image: (info[UIImagePickerControllerOriginalImage] as? UIImage)!, newWidth: 200) on an iPad 9.7". Image taken from imagePickerController. Using swift 3.0 – Brian Bird Nov 07 '17 at 21:32
45

If you're dealing with PNG images that contain transparencies, then the accepted answer function will actually convert the transparent areas to black.

If you wish to scale and keep the transparencies in place, try this function:

SWIFT 4

extension UIImage {
    func scaleImage(toSize newSize: CGSize) -> UIImage? {
        var newImage: UIImage?
        let newRect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height).integral
        UIGraphicsBeginImageContextWithOptions(newSize, false, 0)
        if let context = UIGraphicsGetCurrentContext(), let cgImage = self.cgImage {
            context.interpolationQuality = .high
            let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: newSize.height)
            context.concatenate(flipVertical)
            context.draw(cgImage, in: newRect)
            if let img = context.makeImage() {
                newImage = UIImage(cgImage: img)
            }
            UIGraphicsEndImageContext()
        }
        return newImage
    }
}
Travis M.
  • 10,930
  • 1
  • 56
  • 72
  • 1
    Amazing.. reduced memory footprint from under 200mb to 18 :D – Tom Mar 09 '17 at 13:17
  • 4
    You should also take into consideration the screen scale by replacing the UIImage init with: `let scale = UIScreen.main.scale let newImage = UIImage(cgImage: context.makeImage()!, scale: scale, orientation: .up)` – picciano Jun 29 '17 at 14:52
  • 1
    %30 of users crashes on this function.. at line context.draw(self.cgImage!, in: newRect)... Crashed: com.apple.root.utility-qos 0x1001500a8 specialized UIImage.scaleImage(toSize : CGSize) -> UIImage? – Can Aksoy Jul 07 '17 at 10:49
  • 1
    Thank you, @CanAksoy, I removed the forced optionals, this should help. – Travis M. Sep 25 '18 at 21:37
24

For Swift 3.0

simply add this snippet as extension to UIImage. However, remember that is not going to make the image in square form but if it was in that form, the result will be square.

extension UIImage {
    func resizeImage(newWidth: CGFloat) -> UIImage {

        let scale = newWidth / self.size.width
        let newHeight = self.size.height * scale
        UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
        self.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage!
    } }
Faruk
  • 2,269
  • 31
  • 42
  • Tried this code. It does produce a small;;er image, but it leaves a huge memory print -- allocates some extra 100-200MB on the heap (( – rommex Dec 30 '17 at 16:58
11

Swift 4.0 -

If you're dealing with images that contain transparencies, then the accepted answer function will actually convert the transparent areas to black.

If you wish to scale and keep the transparencies in place, try this function:

func resizeImageWith(image: UIImage, newSize: CGSize) -> UIImage {

    let horizontalRatio = newSize.width / image.size.width
    let verticalRatio = newSize.height / image.size.height

    let ratio = max(horizontalRatio, verticalRatio)
    let newSize = CGSize(width: image.size.width * ratio, height: image.size.height * ratio)
    var newImage: UIImage

    if #available(iOS 10.0, *) {
        let renderFormat = UIGraphicsImageRendererFormat.default()
        renderFormat.opaque = false
        let renderer = UIGraphicsImageRenderer(size: CGSize(width: newSize.width, height: newSize.height), format: renderFormat)
        newImage = renderer.image {
            (context) in
            image.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
        }
    } else {
        UIGraphicsBeginImageContextWithOptions(CGSize(width: newSize.width, height: newSize.height), isOpaque, 0)
        image.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
        newImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
    }

    return newImage
}
abhimanyu jindal
  • 658
  • 8
  • 18
10

This code uses UIGraphicsImageRenderer introduced in iOS 10: in my testing it was 10-40% faster than earlier samples with UIGraphicsBeginImageContext (Swift 4 / Xcode 9):

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

            let renderer = UIGraphicsImageRenderer(size: newSize)

            let image = renderer.image { (context) in
                self.draw(in: CGRect(origin: CGPoint(x: 0, y: 0), size: newSize))
            }
            return image
        }
    }
Lito
  • 2,309
  • 1
  • 23
  • 29
rommex
  • 763
  • 1
  • 8
  • 21
  • There’s already an answer covering this: https://stackoverflow.com/a/45936836/1489885 – HAS Dec 31 '17 at 08:42
  • 1
    @HAS my code is shorter, is done with extension and has my data of performance. – rommex Sep 07 '18 at 07:26
  • Although I really loved this answer (for using modern tools, fast and concise coding) I must point out unexpected behavior I experienced with it. If the original UIImage is cropped from camera, and has some size, (say 1180x850) and I was asking the code to "scale it down" to newWidth:800, it actually TRIPPLED the image size - because my device was 3x retina!!! One MUST provide a UIGraphicsImageRendererFormat with the desired scale!!! Beware. We got huge image instead of smallr. – Motti Shneor May 05 '19 at 13:29
8

This function will return an image with width you specified:

func scaleImage(image: UIImage, maximumWidth: CGFloat) -> UIImage {
    let rect: CGRect = CGRectMake(0, 0, image.size.width, image.size.height)
    let cgImage: CGImageRef = CGImageCreateWithImageInRect(image.CGImage!, rect)!
    return UIImage(CGImage: cgImage, scale: image.size.width / maximumWidth, orientation: image.imageOrientation)
}

Swift 3.0

func scaledImage(_ image: UIImage, maximumWidth: CGFloat) -> UIImage {
    let rect: CGRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
    let cgImage: CGImage = image.cgImage!.cropping(to: rect)!
    return UIImage(cgImage: cgImage, scale: image.size.width / maximumWidth, orientation: image.imageOrientation)
}
Rashid
  • 1,515
  • 16
  • 16
6

This code works excellently on square image and won't lose the quality

extension UIImage {

func resize(targetSize: CGSize) -> UIImage {
    return UIGraphicsImageRenderer(size:targetSize).image { _ in
        self.draw(in: CGRect(origin: .zero, size: targetSize))
    }
}

}

Answer From: Resize Image without losing quality

ninahadi
  • 432
  • 6
  • 9
4

Further improving on @rommex's answer using a maximum size, in Swift 4.2:

private extension UIImage {
    func scaled(to maxSize: CGFloat) -> UIImage? {
        let aspectRatio: CGFloat = min(maxSize / size.width, maxSize / size.height)
        let newSize = CGSize(width: size.width * aspectRatio, height: size.height * aspectRatio)
        let renderer = UIGraphicsImageRenderer(size: newSize)
        return renderer.image { context in
            draw(in: CGRect(origin: CGPoint(x: 0, y: 0), size: newSize))
        }
    }
}
Balázs Vincze
  • 3,550
  • 5
  • 29
  • 60
2
func getScaledDimension(width: CGFloat, height: CGFloat,new_width: CGFloat, new_height: CGFloat)->CGPoint {

        let widthAspect =  (width / new_width)
        let heightAspect = (height / new_height)
        if widthAspect == 0 || heightAspect == 0 {
            return CGPoint(x: width, y: height)
        }
        var width1 : CGFloat = 0
        var height1 : CGFloat =  0
        if widthAspect > heightAspect {
            width1 = (width) / heightAspect
            height1 = (height) / heightAspect
        } else {
            width1 = (width) / widthAspect
            height1 = (height) / widthAspect
        }

        return CGPoint(x: width1, y: height1 )
    }



    func ResizeImage(image: UIImage, targetSize: CGSize) -> UIImage {

        let rect = CGRectMake(0, 0, targetSize.width, targetSize.height)

        UIGraphicsBeginImageContextWithOptions(targetSize, false, 1.0)
        image.drawInRect(rect)
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage
    }


 let imagesize =  getScaledDimension(image.size.width, height: image.size.height , new_width: Width, new_height: Hieght)

        print("Image Size Scaled Dimension -> H:\(imagesize.x) W:\(imagesize.y)")

        let newImage = ResizeImage(image, targetSize: CGSizeMake(imagesize.x,imagesize.y))
        print("Resize Image Size -> H\(newImage.size.height) W\(newImage.size.width) ")
Mohammad Razipour
  • 3,643
  • 3
  • 29
  • 49
2

If you are using kingfisher lib for loading images in you project and want to resize it here is the way:

  • Xcode 8
  • Swift 3x
    let imageUrl = URL(string: "your image url")
     //Size refer to the size which you want to resize your original image
     let size = CGSize(width: 60, height: 60)
     let processImage = ResizingImageProcessor(targetSize: size, contentMode: .aspectFit)
     cell.courseTitleImage.kf.setImage(with: imageUrl! , placeholder: UIImage(named: "placeholder"), options: [.transition(ImageTransition.fade(1)), .processor(processImage)], progressBlock: nil, completionHandler: nil)
    

    OR

    Resize Local Image:- you can refer to the answer of @Christoph R

  • Abdul Karim
    • 4,359
    • 1
    • 40
    • 55
    1

    This is a continuation to @Christoph R 's answer posted for Swift 3.0. This code works for Swift 5.0.1.

    static func resizeImage(image: UIImage, newWidth: CGFloat) -> UIImage {
    
        let scale = newWidth / image.size.width
        let newHeight = image.size.height * scale
        UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
        image.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    
        return newImage!
    }
    

    at callers site

    TaskUtilties.resizeImage(image: rawImage!, newWidth: CGFloat(50))
    
    vikas kumar
    • 10,447
    • 2
    • 46
    • 52
    0

    Reduce image size by 1024, you can always convert according server capacity

    func resizeImage(image: UIImage) -> UIImage {
    
            if image.size.height >= 1024 && image.size.width >= 1024 {
    
                UIGraphicsBeginImageContext(CGSize(width:1024, height:1024))
                image.draw(in: CGRect(x:0, y:0, width:1024, height:1024))
    
                let newImage = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
    
                return newImage!
    
            }
            else if image.size.height >= 1024 && image.size.width < 1024
            {
    
                UIGraphicsBeginImageContext(CGSize(width:image.size.width, height:1024))
                image.draw(in: CGRect(x:0, y:0, width:image.size.width, height:1024))
    
                let newImage = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
    
                return newImage!
    
            }
            else if image.size.width >= 1024 && image.size.height < 1024
            {
    
                UIGraphicsBeginImageContext(CGSize(width:1024, height:image.size.height))
                image.draw(in: CGRect(x:0, y:0, width:1024, height:image.size.height))
    
                let newImage = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
    
                return newImage!
    
            }
            else
            {
                return image
            }
    
        }
    
    Nilesh R Patel
    • 697
    • 7
    • 17
    Urvish Modi
    • 1,118
    • 10
    • 11