Dynamic UITableView with images

2020-04-14 16:00发布

There are similar questions, but non of the answers worked for me. In a dynamic table I want to display images that have different heigh. Each cell has a UIImageView with contentMode = .scaleAspectFit so the image nicely takes the width of the table and takes the height as much as needed.

Cell has 4 constraints: enter image description here

Table view controller:

class TableTableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.estimatedRowHeight = 100
        tableView.rowHeight = UITableViewAutomaticDimension
    }

    override func numberOfSections(in tableView: UITableView) -> Int {

        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return 2
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ImageTableViewCell.self), for: indexPath) as! ImageTableViewCell

        let image = indexPath.row == 0 ? UIImage(named: "1.jpg")! : UIImage(named: "2.jpg")!
        cell.customImageView.image = image

        return cell
    }
}

Result:

enter image description here

As you can see that the height of the cell is incorrect (red background of the image view on top and bottom of the image view). I believe this happens because intrinsicContentSize of the image view is equal to the image size and thats why the height of the cell is calculated incorrectly (content mode is not taken into account). I tried calculating height of the image and adding height constraint for the image view:

cell.heightConstraint.constant = cell.frame.width * image.size.height /  image.size.width

but it breaks cell's content view constraints.

The project can be downloaded here>>

1条回答
▲ chillily
2楼-- · 2020-04-14 16:38

In your ImageTableViewCell.swift

import UIKit

class ImageTableViewCell: UITableViewCell {

    @IBOutlet weak var customImageView: UIImageView!

    internal var aspectConstraint : NSLayoutConstraint? {
        didSet {
            if oldValue != nil {
                customImageView.removeConstraint(oldValue!)
            }
            if aspectConstraint != nil {
                customImageView.addConstraint(aspectConstraint!)
            }
        }
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        aspectConstraint = nil
    }

    func setPostedImage(image : UIImage) {

        let aspect = image.size.width / image.size.height

        aspectConstraint = NSLayoutConstraint(item: customImageView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: customImageView, attribute: NSLayoutAttribute.height, multiplier: aspect, constant: 0.0)

        customImageView.image = image
    }
}

And into your TableTableViewController.swift your cellForRowAt method will be:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "ImageTableViewCell", for: indexPath) as! ImageTableViewCell

    let image = imageArr[indexPath.row]
    cell.setPostedImage(image: image!)
    return cell
}

And declare your imageArr this way:

let imageArr = [UIImage(named: "1.jpg"), UIImage(named: "2.jpg")]

And your compete code will be:

import UIKit

class TableTableViewController: UITableViewController {

    let imageArr = [UIImage(named: "1.jpg"), UIImage(named: "2.jpg")]
    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.estimatedRowHeight = 100
        tableView.rowHeight = UITableViewAutomaticDimension
    }

    override func numberOfSections(in tableView: UITableView) -> Int {

        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return 2
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ImageTableViewCell", for: indexPath) as! ImageTableViewCell

        let image = imageArr[indexPath.row]
        cell.setPostedImage(image: image!)
        return cell
    }
}

And THIS will be your result.

EDIT:

To fix constraint issue set aspectConstraint priority to 999 and aspectConstraint will be:

internal var aspectConstraint : NSLayoutConstraint? {
    didSet {
        if oldValue != nil {
            customImageView.removeConstraint(oldValue!)
        }
        if aspectConstraint != nil {
            aspectConstraint?.priority = 999  //add this
            customImageView.addConstraint(aspectConstraint!)
        }
    }
}
查看更多
登录 后发表回答