mihai mimi
mihai mimi

Reputation: 133

Swift 5 table view cell with UIImage appears very tall and image extremely zoomed

Im working on a chat app, so the height of the rows varies. I am using separate cells for plain txt msg and msg with image (and maybe text). I allow the user to select an image from the phone. I display that image in a separate VC where he can enter text if he chooses and send it. I have a model for the msg which means I do conversion between base64 and image formats. I have tried to simplify to the max my cell class to understand the following problem: the image inside the image view appears zoomed beyond what the normal phone zoom would allow; and the height of the cell is immense. On the cell class that I need to use I have more items and constraints but the basic logic of interest here is below:

    fileprivate func configureMsgsTable() {
    tableView.contentInsetAdjustmentBehavior = .automatic
    tableView.backgroundColor = .clear
    tableView.keyboardDismissMode = .interactive
    tableView.separatorStyle = .none
    tableView.showsVerticalScrollIndicator = false
    tableView.rowHeight = UITableView.automaticDimension
    tableView.estimatedRowHeight = 100
    tableView.sectionFooterHeight = 0.0
}

these are the functions I use for encoding/decoding:

fileprivate func convertImageToBase64String (img: UIImage) -> String {
    let imageData:NSData = img.jpegData(compressionQuality: 0.50)! as NSData
    let imgString = imageData.base64EncodedString(options: Data.Base64EncodingOptions.lineLength64Characters)
    return imgString
}
fileprivate func convertBase64StringToImage (imageBase64String:String) -> UIImage {
    let imageData = Data.init(base64Encoded: imageBase64String, options: Data.Base64DecodingOptions.ignoreUnknownCharacters)
    let image = UIImage(data: imageData!)
    return image!
}

and this is the cell class.

class MsgWithImg: UITableViewCell {
//MARK: - Observer.
internal var valuesToDisplay: NewProjectGrpMsgModel! {
    didSet {
        imgView.image = convertBase64StringToImage(imageBase64String: valuesToDisplay.msgAttachment)
    }
}
//MARK: - Properties.
fileprivate let imgView: UIImageView = {
    let imgView = UIImageView(frame: .zero)
    imgView.clipsToBounds = true
    imgView.translatesAutoresizingMaskIntoConstraints = false
    imgView.contentMode = .scaleAspectFill
    imgView.layer.cornerRadius = 0
    return imgView
}()
//MARK: - Init.
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    backgroundColor = .clear
    clipsToBounds = true
    selectionStyle = .none
    setupViews()
}
required init?(coder: NSCoder) {fatalError("init(coder:) has not been implemented")}
fileprivate func setupViews() {
    contentView.addSubview(imgView)
    let imgViewConstraints = [
        imgView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 3),
        imgView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -3),
        imgView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
        imgView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -45)
    ]
    NSLayoutConstraint.activate(imgViewConstraints)
}

} I spent some time thinking that this is an auto layout problem; or the fact that the table row height is automatic. that's why I built this test cell class with only the image view. but I think this problem is of a different nature. I did read quite a few answers to what I could find relevant on this website but I cannot determine what the problem is and the console does not print out anything.

Upvotes: 0

Views: 914

Answers (1)

DonMag
DonMag

Reputation: 77477

The problem is that if you don't give a UIImageView both a width and a height, its intrinsicContentSize becomes the size of the image assigned to it.

With your code as-is, you've given the image view a width by constraining its Leading and Trailing anchors, but you haven't given it a height -- either by itself or by the cell's height (since you want auto-sizing cells).

So, if we use these four images:

enter image description here

enter image description here

enter image description here

enter image description here

The resulting table view looks like this:

enter image description here

enter image description here

And here's what's happening on an iPhone 13 (note: all sizes are rounded)...

For the 100x200 image:

  • your Leading/Trailing constraints make the image view frame 345-pts wide
  • no height set, so auto-layout uses the image size (200-pts), setting the image view frame 200-pts tall
  • image view is set to .scaleAspectFill, so the scaled size is 345 x 690

For the 100x300 image:

  • your Leading/Trailing constraints make the image view frame 345-pts wide
  • no height set, so auto-layout uses the image size (300-pts), setting the image view frame 300-pts tall
  • image view is set to .scaleAspectFill, so the scaled size is 345 x 1035

For the 600x200 image:

  • your Leading/Trailing constraints make the image view frame 345-pts wide
  • no height set, so auto-layout uses the image size (200-pts), setting the image view frame 200-pts tall
  • image view is set to .scaleAspectFill, so the scaled size is 600 x 200

For the 800x600 image:

  • your Leading/Trailing constraints make the image view frame 345-pts wide
  • no height set, so auto-layout uses the image size (600-pts), setting the image view frame 600-pts tall
  • image view is set to .scaleAspectFill, so the scaled size is 800 x 600

It may be clearer if we set the image view to .scaleAspectFit (with a red background so we can see the frame):

enter image description here

enter image description here

As a general rule, it is common to give the image view a fixed size (or proportional size), and use .scaleAspectFit to show the complete images. Or, also common, to use a pre-processor to generate "thumbnail" sized images for the table view cells.

Upvotes: 1

Related Questions