All I'm doing in my cellForRowAt
is setting UITableViewCell's (no subclass) imageView?.image and textLabel?.text values (and fonts and colors). Setting an image via SF Symbols and text according to my model. To go into more detail, there are only three possible cell kinds: a workspace, the "Add Workspace" button, and the archive.
In the following screenshot, I've demonstrated with green lines how these views don't quite line up in a logical fashion. Text is misaligned between all three types of cells, and (annoyingly) the SF Symbols image for that boxy icon with an "add" (+) indicator is slightly wider than the standard image.
Can anyone help me with an easy fix for this that I'm simply just missing? I've already tried setting the imageViews' aspect ratios to 1:1 with constraints. That didn't affect anything.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell
if indexPath.section == 0 {
cell = tableView.dequeueReusableCell(withIdentifier: "workspace", for: indexPath)
if indexPath.row == addWorkspaceRow {
cell.imageView?.image = .addWorkspace
cell.imageView?.tintColor = cell.tintColor
cell.textLabel?.text = "Add Workspace"
cell.textLabel?.textColor = cell.tintColor
cell.accessoryType = .none
} else {
let workspace = model.workspaces[indexPath.row]
cell.imageView?.image = .workspace
cell.imageView?.tintColor = .label
cell.textLabel?.text = workspace.displayName
cell.textLabel?.textColor = .label
cell.accessoryType = .disclosureIndicator
}
} else {
cell = tableView.dequeueReusableCell(withIdentifier: "archive", for: indexPath)
cell.imageView?.image = .archive
cell.imageView?.tintColor = .archive
cell.textLabel?.text = "Archive"
cell.textLabel?.textColor = .label
cell.accessoryType = .disclosureIndicator
}
cell.textLabel?.font = .preferredFont(forTextStyle: .body)
return cell
}
I meet the same things, and want to solve it without using custom cell. But it is hard to config the constraint without subclass it.
Eventually, I turn to use custom cell, it is simple to use, and in case you may need to add more stuff in your tableView cell, custom cell is the better place to go.
class LibraryTableCell: UITableViewCell {
var cellImageView = UIImageView()
var cellLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: "libraryTableCell")
cellImageView.translatesAutoresizingMaskIntoConstraints = false
cellImageView.contentMode = .scaleAspectFit
cellImageView.tintColor = .systemPink
contentView.addSubview(cellImageView)
cellLabel.translatesAutoresizingMaskIntoConstraints = false
cellLabel.font = UIFont.systemFont(ofSize: 20)
contentView.addSubview(cellLabel)
NSLayoutConstraint.activate([
cellImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
cellImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
cellImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
cellImageView.widthAnchor.constraint(equalToConstant: 44),
cellLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
cellLabel.leadingAnchor.constraint(equalTo: cellImageView.trailingAnchor, constant: 10),
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class LibraryViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView = UITableView()
let cellTitles = ["Playlists", "Artists", "Albums", "Songs", "Genres"]
let imageNames = ["music.note.list", "music.mic", "square.stack", "music.note", "guitars"]
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
title = "Library"
navigationController?.navigationBar.prefersLargeTitles = true
tableView.delegate = self
tableView.dataSource = self
tableView.register(LibraryTableCell.self, forCellReuseIdentifier: "libraryTableCell")
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.rowHeight = 48
view.addSubview(tableView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
tableView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
tableView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20),
tableView.heightAnchor.constraint(equalTo: g.heightAnchor, multiplier: 0.6)
])
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellTitles.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "libraryTableCell", for: indexPath) as? LibraryTableCell else {
fatalError("Unable to dequeue libraryTableCell")
}
cell.accessoryType = .disclosureIndicator
let imageName = imageNames[indexPath.row]
cell.cellImageView.image = UIImage(systemName: imageName)
let title = cellTitles[indexPath.row]
cell.cellLabel.text = title
return cell
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.separatorInset = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 8)
}
}