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?
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:
It looks like this when running: