What I want to achive is something like list view in Files App. Document will display a little thumbnail if has one. Otherwise a SF Symbol with textStyle .callout
will show up. All the rows’ labels should be left aligned. But currently the thumbnails are much bigger than the SF Symbols so that they push the labels away.
I emphasize textStyle because my app supports dynamic type, which means the imageView
's frame calculation should base on SF Symbol.
I try to override layoutSubviews
. But can't figure out how to do the calculation.
For maximum control of your cell appearance, you should create a custom cell. But, if you want use a standard Subtitle
cell, you can resize your images when setting them.
There are some excellent image scaling functions in this Stack Overflow answer: how to resize a bitmap on iOS
Add the extensions from that answer to your project. Specifically, for your needs, we'll use scaledAspectFit()
.
So, assuming you have a Subtitle
cell prototype, with identifier of "cell", your cellForRowAt
will look something like this:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
// however you have your data stored
let title = "IMG_0001.JPG"
let subtitle = "Today at 10:15 AM, 2.5 MB"
let imgName = "IMG_0001"
cell.textLabel?.text = title
cell.detailTextLabel?.text = subtitle
if let img = UIImage(named: imgName) {
// default cell image size is 56x56 (points)
// so we'll proportionally scale the image,
// using screen scale to get the best resolution
let widthHeight = UIScreen.main.scale * 56
let scaledImg = img.scaledAspectFit(to: CGSize(width: widthHeight, height: widthHeight))
cell.imageView?.image = scaledImg
}
return cell
}
Edit
Takes only a little research to implement the use fo SF Symbols...
Add this func to your table view controller - it will return a UIImage
of an SF Symbol at specified point size, centered in specified size:
private func drawSystemImage(_ sysName: String, at pointSize: CGFloat, centeredIn size: CGSize) -> UIImage? {
let cfg = UIImage.SymbolConfiguration(pointSize: pointSize)
guard let img = UIImage(systemName: sysName, withConfiguration: cfg) else { return nil }
let x = (size.width - img.size.width) * 0.5
let y = (size.height - img.size.height) * 0.5
let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image { context in
img.draw(in: CGRect(origin: CGPoint(x: x, y: y), size: img.size))
}
}
Then change your cellForRowAt
func as needed:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
// however you have your data stored
let title = "IMG_0001.JPG"
let subtitle = "Today at 10:15 AM, 2.5 MB"
let imgName = "IMG_0001"
cell.textLabel?.text = title
cell.detailTextLabel?.text = subtitle
// defalt image view size
let wh = UIScreen.main.scale * 56
let targetSize = CGSize(width: wh, height: wh)
// for this example, if it's the first row, generate an image from SF Symbol
if indexPath.row == 0 {
if let img = drawSystemImage("doc.text", at: UIScreen.main.scale * 24, centeredIn: targetSize) {
cell.imageView?.image = img
}
} else {
if let img = UIImage(named: imgName) {
// default cell image size is 56x56 (points)
// so we'll proportionally scale the image,
// using screen scale to get the best resolution
let scaledImg = img.scaledAspectFit(to: targetSize)
cell.imageView?.image = scaledImg
}
}
return cell
}
I'll leave it up to you to tweak the point size as desired (and configure any other properties of your SF Symbol image such as weight, scale, color, etc).