UIImageview is not scaling to its frame when image is set

37 views Asked by At

enter image description here

Here I have attached the screenshot that I have worked. I am facing an issue that image view is not scaling to the frame. I have created a custom view within the custom view i have added imageview and set constraints to its parent view (here containerview)

image content mode is scaleAspectFit

let imageView = makeImageView()
addSubview(imageView)

imageView.topAnchor.constraint(equalTo: topAnchor).isActive = true
imageView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
imageView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true

When I initialise the custom view into the viewcontroller shows like above screenshots. In viewcontroller I am just adding the image to the stackview.

Can you please let me know what should be added?

1

There are 1 answers

0
DonMag On BEST ANSWER

You need to leave the image view's content mode set to .contentMode = .scaleToFill.

Then, when you set its .image, set a proportional height anchor with:

multiplier: img.size.height / img.size.width)

Here is an example custom class - I've inset the image view by 8-points on all 4 sides for clarity:

class ProportionalImageView: UIView {
    
    public var image: UIImage? {
        didSet {
            imgView.image = image
            if let img = image {
                proConstraint.isActive = false
                proConstraint = imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: img.size.height / img.size.width)
                proConstraint.isActive = true
            }
        }
    }
    
    private var proConstraint: NSLayoutConstraint!
    
    private let imgView = UIImageView()

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    private func commonInit() {

        imgView.contentMode = .scaleToFill
        imgView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(imgView)
    
        proConstraint = imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: 1.0)
        
        NSLayoutConstraint.activate([
            imgView.topAnchor.constraint(equalTo: topAnchor, constant: 8.0),
            imgView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8.0),
            imgView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8.0),
            imgView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8.0),
            proConstraint,
        ])
        
        backgroundColor = .systemBlue
    }
    
}

and an example view controller... vertical stack view containing a "top label" the custom view and a "bottom label". Also loads 3 images - at 300x300, 300x200 and 200x300. Tapping anywhere cycles through to the next image:

class ProImageTestVC: UIViewController {
    
    let proImageView = ProportionalImageView()
    
    var images: [UIImage] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        ["i300x300", "i200x300", "i300x200"].forEach { sName in
            guard let img = UIImage(named: sName) else {
                fatalError("Could not load image: \(sName)")
            }
            images.append(img)
        }
        
        let stackView = UIStackView()
        stackView.axis = .vertical
    
        let v1 = UILabel()
        v1.text = "Top Label"
        v1.textAlignment = .center
        v1.backgroundColor = .green
        
        let v2 = UILabel()
        v2.text = "Bottom Label"
        v2.textAlignment = .center
        v2.backgroundColor = .green
        
        stackView.addArrangedSubview(v1)
        stackView.addArrangedSubview(proImageView)
        stackView.addArrangedSubview(v2)

        stackView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(stackView)
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 60.0),
            stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -60.0),
        ])
        
        nextImage()
    }

    func nextImage() {
        let img = images.removeFirst()
        images.append(img)
        proImageView.image = img
    }
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        nextImage()
    }
    
}

Using these three images:

enter image description here

enter image description here

enter image description here

It looks like this when running:

enter image description here

enter image description here

enter image description here