Reputation: 1
I initialised my UITableViewCell as with a initLayout function as follows:
contentView.addSubview(horizontalStack)
horizontalStack.addArrangedSubview(imageToView)
imageToView.contentMode = .scaleAspectFit
verticalStack.addArrangedSubview(petName)
verticalStack.addArrangedSubview(petDescription)
horizontalStack.addArrangedSubview(verticalStack)
but this gives me the following results. The image is way too big and the lines are all messed up, with the title way up above at the top:
I want the view to look something like this. Note the symmetric nature of all the elements
What I have tried so far:
Can someone please help me with this?
Upvotes: 0
Views: 61
Reputation: 77423
Stack views do a great job of arranging subviews - but you have to give them enough information so they know how you want them arranged.
First, I'm assuming you added constraints for the horizontalStack
to begin with (otherwise we wouldn't see anything in your screen caps).
Without providing any other constraints, stack views use the intrinsicContentSize
of the subviews to handle the arrangement. If you haven't given a UIImageView
any constraints, its intrinsic size will be the size of the image.
Assuming you want the image to be square (1:1 ratio), give it a heightAnchor = widthAnchor
constraint.
imageToView.heightAnchor.constraint(equalTo: imageToView.widthAnchor)
Then, decide how wide you want it to be. A constant point width - such as 80?
imageToView.widthAnchor.constraint(equalToConstant: 80.0)
Or a relative width, such as 1/2 the width of the labels?
imageToView.widthAnchor.constraint(equalTo: verticalStack.widthAnchor, multiplier: 0.5)
You'll also want to decide how you want the alignment in your horizontalStack
.
Here are a couple examples. The first section has horizontalStack.alignment = .center
and the second section uses horizontalStack.alignment = .top
(images on right have background colors and a dashed-border around the stack view to make it easy to see the frames):
Here is the code I used - no @IBoutlet
connections or prototype cells, so just add a table view controller and assign the class to PatroTableViewController
:
class PatroCell: UITableViewCell {
static let identifier: String = "patroCell"
let horizontalStack: UIStackView = {
let v = UIStackView()
v.spacing = 8
v.alignment = .center
return v
}()
let verticalStack: UIStackView = {
let v = UIStackView()
v.axis = .vertical
v.spacing = 8
return v
}()
let imageToView: UIImageView = {
let v = UIImageView()
return v
}()
let petName: UILabel = {
let v = UILabel()
v.font = UIFont.systemFont(ofSize: 16.0)
return v
}()
let petDescription: UILabel = {
let v = UILabel()
v.font = UIFont.systemFont(ofSize: 15.0)
v.textColor = .lightGray
v.numberOfLines = 0
return v
}()
// so we can see the frame of the horizontal stack view
let stackOutlineView: DashedOutlineView = {
let v = DashedOutlineView()
return v
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func commonInit() -> Void {
stackOutlineView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(stackOutlineView)
horizontalStack.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(horizontalStack)
horizontalStack.addArrangedSubview(imageToView)
imageToView.contentMode = .scaleAspectFit
verticalStack.addArrangedSubview(petName)
verticalStack.addArrangedSubview(petDescription)
horizontalStack.addArrangedSubview(verticalStack)
let g = contentView.layoutMarginsGuide
// this will avoid auto-layout warnings
let hsBottom = horizontalStack.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0)
hsBottom.priority = UILayoutPriority(rawValue: 999)
NSLayoutConstraint.activate([
// constrain horizontal stack to all 4 sides - use margins
horizontalStack.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
horizontalStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
horizontalStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
// the bottom anchor with priority 999
hsBottom,
// image view should be square - 1:1 ratio
imageToView.heightAnchor.constraint(equalTo: imageToView.widthAnchor),
// image view should be 80 x 80?
//imageToView.widthAnchor.constraint(equalToConstant: 80.0),
// or, maybe, image view width should be 1/2 the width of the labels?
imageToView.widthAnchor.constraint(equalTo: verticalStack.widthAnchor, multiplier: 0.5),
// constrain the outline view
stackOutlineView.topAnchor.constraint(equalTo: horizontalStack.topAnchor, constant: 0.0),
stackOutlineView.leadingAnchor.constraint(equalTo: horizontalStack.leadingAnchor, constant: 0.0),
stackOutlineView.trailingAnchor.constraint(equalTo: horizontalStack.trailingAnchor, constant: 0.0),
stackOutlineView.bottomAnchor.constraint(equalTo: horizontalStack.bottomAnchor, constant: 0.0),
])
stackOutlineView.isHidden = true
// change to "if true" to see the frames
if false {
// so we can see the UI element frames
imageToView.backgroundColor = .red
petName.backgroundColor = .green
petDescription.backgroundColor = .yellow
petDescription.textColor = .black
stackOutlineView.isHidden = false
}
}
}
class PatroTableViewController: UITableViewController {
let descriptions: [String] = [
"One-line description",
"This is the description of the pet in this cell. It is enough text that it will cause word-wrapping.",
"This description will be much longer... It will wrap onto many lines so we can see how the cell layout will look when the description text makes the label taller than the image view on the left. Note the differences between \".alignment = .center\" vs \".alignment = .top\""
]
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(PatroCell.self, forCellReuseIdentifier: PatroCell.identifier)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return descriptions.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: PatroCell.identifier, for: indexPath) as! PatroCell
cell.petName.text = "Dog"
cell.petDescription.text = descriptions[indexPath.row]
cell.imageToView.image = UIImage(named: "dog")
if indexPath.section == 0 {
// set horizontal stack alignment to center
cell.horizontalStack.alignment = .center
} else {
// set horizontal stack alignment to top
cell.horizontalStack.alignment = .top
}
return cell
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Alignment: Center"
}
return "Alignment: Top"
}
}
class DashedOutlineView: UIView {
var shapeLayer: CAShapeLayer!
override class var layerClass: AnyClass {
return CAShapeLayer.self
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func commonInit() -> Void {
shapeLayer = self.layer as? CAShapeLayer
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor(red: 0.0, green: 0.75, blue: 0.0, alpha: 1.0).cgColor
shapeLayer.lineWidth = 1.0
shapeLayer.lineDashPattern = [8,8]
}
override func layoutSubviews() {
super.layoutSubviews()
shapeLayer.path = UIBezierPath(rect: bounds).cgPath
}
}
Upvotes: 1