I would approach the View hierarchy a little bit differently to achieve what you want.
You seem to have
- a background UIView
- a UIImageView on top of the background view which needs to be a square
- A UILabel below the background view
It will be quite difficult to get views to overlap using stack views.
What I suggest is to create a custom UIView with the UIView, UIImageView and the UILabel and put these custom views inside a stack view.
Inside your custom view implementation, you need to figure out how to make the UIImageView a square.
Here is how I implement this custom view:
class CustomView: UIView
{
private var imageView: UIImageView!
private let someView = UIView()
private let label = UILabel()
init()
{
super.init(frame: .zero)
configureSubView()
configureImageView()
configureLabel()
}
required init?(coder: NSCoder)
{
fatalError("init(coder:) has not been implemented")
}
// Configure the background view behind the image
private func configureSubView()
{
someView.backgroundColor = randomColor()
someView.translatesAutoresizingMaskIntoConstraints = false
addSubview(someView)
// The background view pins to the edges of the custom view
// and has a height of 70% of the custom view. Adjust these as
// you see fit
someView.leadingAnchor.constraint(equalTo: leadingAnchor)
.isActive = true
someView.topAnchor.constraint(equalTo: topAnchor)
.isActive = true
someView.trailingAnchor.constraint(equalTo: trailingAnchor)
.isActive = true
someView.heightAnchor.constraint(equalTo: heightAnchor,
multiplier: 0.7).isActive = true
}
private func configureImageView()
{
// Replace with your image here
if let image = UIImage(systemName: "scribble")?
.withTintColor(randomColor(), renderingMode: .alwaysOriginal)
{
// Assume image is a square and get dimension of a side
let side = image.size.width
// Initialize your image with the UIImage
imageView = UIImageView(image: image)
// added bg color so you can see impact of making it circular
imageView.backgroundColor = randomColor()
imageView.translatesAutoresizingMaskIntoConstraints = false
someView.addSubview(imageView)
// Auto layout constraints to make it a square
imageView.widthAnchor.constraint(equalToConstant: side)
.isActive = true
imageView.heightAnchor.constraint(equalToConstant: side)
.isActive = true
imageView.centerXAnchor.constraint(equalTo: someView.centerXAnchor)
.isActive = true
imageView.centerYAnchor.constraint(equalTo: someView.centerYAnchor)
.isActive = true
// apply your corner radius to make image view a circle
imageView.layer.cornerRadius = side / 2
imageView.clipsToBounds = true
}
}
// Configure the label to be below the background view
private func configureLabel()
{
label.text = "Title"
label.textColor = randomColor()
label.backgroundColor = randomColor()
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
label.leadingAnchor.constraint(equalTo: leadingAnchor)
.isActive = true
label.topAnchor.constraint(equalTo: someView.bottomAnchor)
.isActive = true
label.trailingAnchor.constraint(equalTo: trailingAnchor)
.isActive = true
label.bottomAnchor.constraint(equalTo: bottomAnchor)
.isActive = true
}
// https://stackoverflow.com/a/21130486/1619193
// You can ignore this function
private func randomColor() -> UIColor
{
let red = CGFloat(arc4random_uniform(256)) / 255.0
let blue = CGFloat(arc4random_uniform(256)) / 255.0
let green = CGFloat(arc4random_uniform(256)) / 255.0
return UIColor(red: red, green: green, blue: blue, alpha: 1.0)
}
}
Then here is how I put the custom views into a vertical stack view:
class StackViewTestViewController: UIViewController
{
// Stack view to hold the custom views
let verticalStackView = UIStackView()
// Set how many ever you want
let numberOfCustomViews = 3
// Set width of each custom view in stack view
let heightOfCustomView: CGFloat = 80
// Set width of each custom view
let widthOfCustomView: CGFloat = 100
// Set padding between custom views in the stack view
let padding: CGFloat = 20
override func viewDidLoad()
{
super.viewDidLoad()
title = "Square Stack View"
view.backgroundColor = .white
configureStackView()
addCustomViewsToStackView()
}
private func configureStackView()
{
verticalStackView.axis = .vertical
verticalStackView.distribution = .equalSpacing
verticalStackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(verticalStackView)
// Calculate the height of the stack view depending on the custom views
let heightOfStackView = (CGFloat(numberOfCustomViews) * heightOfCustomView)
+ padding
// Auto-layout for the stack view
verticalStackView.heightAnchor.constraint(equalToConstant: heightOfStackView)
.isActive = true
verticalStackView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
.isActive = true
verticalStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
.isActive = true
// do not give width, let the content determine it
}
private func addCustomViewsToStackView()
{
for _ in 0 ..< numberOfCustomViews
{
let customView = CustomView()
customView.translatesAutoresizingMaskIntoConstraints = false
verticalStackView.addArrangedSubview(customView)
// Set width constraint of custom view
customView.widthAnchor.constraint(equalToConstant: widthOfCustomView)
.isActive = true
// Set height constraint of custom view
customView.heightAnchor.constraint(equalToConstant: heightOfCustomView)
.isActive = true
}
}
}
This is the end result with the UIImageView a square and made circular in a vertical stack view which I believe is what you wanted:
