Home > Enterprise >  Image view does not resize according to a new image
Image view does not resize according to a new image

Time:11-02

Let's say one has a placeholder image, which is loaded into an UIImageView. Then, an async operation eventually loads an image with a different size.

The issue I am observing is that I don't know how to make imageView to "wrap" the new image, according to its size, preserving the image original aspect ratio.

Note that when using contentMode=.scaleAspect*, the view scales the image to fit its view. In my case, I want the view to fit the image.

import Foundation
import UIKit

class ViewController: UIViewController {
    let imageView = UIImageView()

    override func loadView() {
        let view = UIView()
        view.addSubview(imageView)
        imageView.contentMode = .scaleAspectFill
        imageView.backgroundColor = .red
        imageView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            imageView.widthAnchor.constraint(equalToConstant: 100),
            imageView.heightAnchor.constraint(equalToConstant: 100),
        ])

        self.view = view
    }
    override func viewDidLoad() {
//        imageView.image = // some placeholder image is shown; size is 100 x 100
//        imageView.image = // network call is made which eventually loads a different image with dynamic size
    }
}

// Present the view controller in the Live View window
import PlaygroundSupport
PlaygroundPage.current.liveView = ViewController()

CodePudding user response:

One approach is to create a NSLayoutConstraint var/property to use as the image view's Height constraint. Then, when the new image comes in, de-activate the constraint, re-create it with the new aspect-ratio (multiplier), and re-activate it.

Here's a quick example...

It starts with an empty image view (red background), at 1:1 ratio.

Each time you tap (well, in playground, click), we'll use a new SF Symbol to simulate getting a new image from a server.

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {
    
    let imageView = UIImageView()
    
    // we'll update the image view height constraint when a new image comes in
    var ivHeight: NSLayoutConstraint!
    
    let sysNames: [String] = [
        "ruler",                    // wide and short
        "flashlight.off.fill",      // tall and narrow
        "folder",                   // square-ish
    ]
    var idx: Int = -1
    
    override func loadView() {
        let view = UIView()
        view.addSubview(imageView)
        //imageView.contentMode = .scaleAspectFill
        imageView.contentMode = .scaleToFill
        imageView.backgroundColor = .red
        imageView.translatesAutoresizingMaskIntoConstraints = false

        ivHeight = imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: 1.0)

        NSLayoutConstraint.activate([
            imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            imageView.widthAnchor.constraint(equalToConstant: 100),
            ivHeight,
        ])
        
        self.view = view
    }
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        // to simulate getting an image from a server...
        
        idx  = 1
        let sysName = sysNames[idx % sysNames.count]
        
        let cfg = UIImage.SymbolConfiguration(pointSize: 120.0, weight: .bold, scale: .large)
        
        guard let sysImg = UIImage(systemName: sysName, withConfiguration: cfg)?.withTintColor(.systemBlue, renderingMode: .alwaysOriginal) else {
            print("failed to create image \(sysName)")
            return
        }

        // set the image
        imageView.image = sysImg

        // get the size of the image
        let sz = sysImg.size

        // de-activate the image view height constraint
        ivHeight.isActive = false

        // set the new image view height constraint, using
        //  image size height / width
        //  as the multiplier
        ivHeight = imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: sz.height / sz.width)
    
        // re-activate the image view height constraint
        ivHeight.isActive = true
    }
    
}

// Present the view controller in the Live View window
PlaygroundPage.current.liveView = ViewController()

It will give you these results... the image view will always have a width of 100, but the height will change to match the aspect ratio of the image:

enter image description here

  • Related