Search code examples
iosswiftuiimageswift2uigraphicscontext

Scaling down UIImage and keep it sharp


I'm using the answer from this question to scale down an image and make it not blurry. However, that is not working for me. I'm using Swift. I do everything like in the answer. This is the code of the function that I use :

func resizeImage(image: UIImage, newSize: CGSize) -> (UIImage) {
        let newRect = CGRectIntegral(CGRectMake(0,0, newSize.width, newSize.height))
        let imageRef = image.CGImage

        UIGraphicsBeginImageContextWithOptions(newSize, false, 0)
        let context = UIGraphicsGetCurrentContext()

        // Set the quality level to use when rescaling
        CGContextSetInterpolationQuality(context, CGInterpolationQuality.High)
        let flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height)

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

        let newImageRef = CGBitmapContextCreateImage(context)! as CGImage
        let newImage = UIImage(CGImage: newImageRef)

        // Get the resized image from the context and a UIImage
        UIGraphicsEndImageContext()

        return newImage
    }

And this is where I call it :

func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {

        let identifier = "mypin"

        if annotation.isKindOfClass(MKUserLocation) {
            return nil
        }

        let detailButton: UIButton = UIButton(type: UIButtonType.DetailDisclosure)

        var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)

        if annotationView == nil
        {
            annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "pin")
            annotationView!.canShowCallout = true
            let pinImage = UIImage(named: "Pin.png")
            let resizedImage = resizeImage(pinImage!, newSize: CGSize(width: 12, height: 20))
            annotationView?.image = resizedImage
            annotationView!.rightCalloutAccessoryView = detailButton
        }
        else
        {
            annotationView!.annotation = annotation
        }

        return annotationView
    }

If anybody knows why this could be happening I would really appreciate it if you could let me know. Thanks in advance.


Solution

  • As I suspected in my comment, it's due to the fact that CGBitmapContextCreateImage will ignore the scale that you input into UIGraphicsBeginImageContextWithOptions – and instead always create an image with a scale of 1.0.

    For example if you have a 50 x 50 image context at a 3x scale:

    • CGBitmapContextCreateImage will return a 150 x 150 image at 1x scale

    • UIGraphicsGetImageFromCurrentImageContext will return a 50 x 50 image at 3x scale

    Usually this shouldn't actually make a difference when displaying the image on-screen, unless you rely on the size to be specified in points rather than pixels. However, for an MKAnnotationView, the documentation says:

    Assigning a new image to this property also changes the size of the view’s frame so that it matches the width and height of the new image.

    Therefore if you assign it an image with a 1x scale (and your screen's scale is higher than 1x) – it will be unnecessarily scaled up, therefore losing quality. The fix therefore is to use UIGraphicsGetImageFromCurrentImageContext.