Is it possible to use the aspect fill content mode combined with the top content mode in UIImageView?

Update: device scaling is now properly handled, thanks to budidino for that!

You should resize the image, so that it will have the width of your image view, but by keeping its aspect ratio. After that, set the image view's content mode to .top and enable clipping to bounds for it.

The resizeTopAlignedToFill function is a modified version of this answer.

func setImageView() {
    imageView.contentMode = .top
    imageView.clipsToBounds = true

    let image = <custom image>
    imageView.image = image.resizeTopAlignedToFill(newWidth: imageView.frame.width)
}

extension UIImage {
    func resizeTopAlignedToFill(newWidth: CGFloat) -> UIImage? {
        let newHeight = size.height * newWidth / size.width

        let newSize = CGSize(width: newWidth, height: newHeight)

        UIGraphicsBeginImageContextWithOptions(newSize, false, UIScreen.main.scale)
        draw(in: CGRect(origin: .zero, size: newSize))
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage
    }
}

Try this:

imageView.contentMode = UIViewContentModeTop;
imageView.image = [UIImage imageWithCGImage:image.CGImage scale:image.size.width / imageView.frame.size.width orientation:UIImageOrientationUp];

The accepted answer was scaling down the image and therefore lowering the quality on @2x and @3x devices. This should produce the same result with better image quality:

extension UIImage {
  func resizeTopAlignedToFill(containerSize: CGSize) -> UIImage? {
    let scaleTarget = containerSize.height / containerSize.width
    let scaleOriginal = size.height / size.width

    if scaleOriginal <= scaleTarget { return self }

    let newHeight = size.width * scaleTarget
    let newSize = CGSize(width: size.width, height: newHeight)

    UIGraphicsBeginImageContextWithOptions(newSize, false, scale)
    self.draw(in: CGRect(origin: .zero, size: newSize))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage
  }
}

then to use it just:

imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.image = UIImage(named: "portrait").resizeTopAlignedToFill(containerSize: imageView.frame.size)

Tags:

Ios

Image