How to align UILabels with SF Symbols and custom images in UITableViewCell's imageView?

655 Views Asked by At

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.

Files App

Files App

My App

My App

1

There are 1 best solutions below

2
On BEST ANSWER

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).