UIImage scaling
image optimization
iOS development
sharpness enhancement
image processing

How to scale down a UIImage and make it crispy / sharp at the same time instead of blurry?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A downscaled image looks blurry on iOS for two common reasons: the resize was done at the wrong pixel size, or the image is still being resampled again at display time. A sharp result comes from deciding the final pixel dimensions first, resizing once with a high-quality algorithm, and then showing the image at exactly that size.

Start with the target pixel size, not just the view size

UIImage.size is measured in points, not pixels. Retina devices then apply the image scale on top of that. If you downscale without thinking about the final pixel dimensions, you can end up with an image that gets resized twice and softens each time.

If a UIImageView is 100 by 100 points on a 2x screen, the crisp target is usually 200 by 200 pixels. That is the image you should generate if you want a perfect fit with no extra interpolation.

Use UIGraphicsImageRenderer for normal app-side resizing

For many cases, UIGraphicsImageRenderer is the simplest high-quality solution. The important part is to render at the correct scale and preserve aspect ratio.

swift
1import UIKit
2
3func resizedImage(_ image: UIImage, targetSize: CGSize, screenScale: CGFloat = UIScreen.main.scale) -> UIImage {
4    let widthRatio = targetSize.width / image.size.width
5    let heightRatio = targetSize.height / image.size.height
6    let scaleFactor = min(widthRatio, heightRatio)
7
8    let drawSize = CGSize(
9        width: image.size.width * scaleFactor,
10        height: image.size.height * scaleFactor
11    )
12
13    let format = UIGraphicsImageRendererFormat.default()
14    format.scale = screenScale
15    format.opaque = false
16
17    let renderer = UIGraphicsImageRenderer(size: drawSize, format: format)
18    return renderer.image { _ in
19        image.draw(in: CGRect(origin: .zero, size: drawSize))
20    }
21}

This works well because the renderer creates an output image at the exact display scale you intend to use.

Avoid resizing in the image view if you care about sharpness

A common anti-pattern is assigning a very large image to a small UIImageView and letting UIKit scale it every time the view draws. That is convenient, but it is not the sharpest path and it wastes memory.

Precompute the image near its final display size instead. Then set the image view frame or constraints so the displayed point size matches the generated asset.

In other words, do the high-quality resize once, not on every frame.

Use Lanczos when image quality matters more than simplicity

If the image contains fine detail such as text, UI screenshots, or line art, a better resampling filter can help. Core Image offers Lanczos scaling, which is often visibly better than naive drawing.

swift
1import UIKit
2import CoreImage
3import CoreImage.CIFilterBuiltins
4
5func lanczosResize(_ image: UIImage, scale: CGFloat) -> UIImage? {
6    guard let cgImage = image.cgImage else { return nil }
7
8    let input = CIImage(cgImage: cgImage)
9    let context = CIContext()
10    let filter = CIFilter.lanczosScaleTransform()
11    filter.inputImage = input
12    filter.scale = Float(scale)
13    filter.aspectRatio = 1.0
14
15    guard let output = filter.outputImage,
16          let result = context.createCGImage(output, from: output.extent) else {
17        return nil
18    }
19
20    return UIImage(cgImage: result, scale: image.scale, orientation: image.imageOrientation)
21}

Lanczos is especially useful for non-photographic images where blur is easy to notice.

Sharpness is also about the source image

No resizing algorithm can invent detail that is not present. If the source image is already too small, compressed heavily, or blurred, downscaling will not somehow make it crisp. Good output starts with enough source resolution.

Likewise, repeated encode and resize cycles degrade quality. Load the original once, resize once, cache the result, and reuse it.

Match content mode to your resize strategy

Even a well-resized image can look soft if the view still scales it unexpectedly. For example, scaleAspectFit preserves the whole image but may leave extra space, while scaleAspectFill may crop. Neither is wrong, but the generated image should match the intended display behavior.

If you generate a 200 by 200 pixel thumbnail and then show it inside a different aspect ratio container, UIKit will resample again and you lose some of the crispness you just worked to preserve.

Common Pitfalls

  • Resizing to the wrong unit by confusing points with pixels.
  • Letting a UIImageView downscale a large image repeatedly at render time.
  • Generating a resized image and then displaying it at a different aspect ratio.
  • Expecting a blurry or low-resolution source image to become sharp after resizing.
  • Re-encoding the same image several times and accumulating quality loss.

Summary

  • Decide the final pixel dimensions before resizing.
  • Use UIGraphicsImageRenderer for a simple high-quality resize path.
  • Use Lanczos scaling when fine detail needs better resampling.
  • Display the resized image at the same size it was generated for.
  • Cache the final resized image so you do the expensive work only once.

Course illustration
Course illustration

All Rights Reserved.