iosswiftuitableviewautoresizeheightforrowatindexpath

Why are my tableview cells not properly sizing when an image is present?


I'm setting up a "newsfeed" styled tableview and would like for my custom cells to automatically resize based on two factors:

1) Whether or not the represented post contains an image - If it doesn't, the cell should behave as there though is no ImageView and size appropriately

2) If the post does contain an image, the ImageView should set its height according to the image height (hence, no height constraint on the ImageView) and the cell should resize accordingly

I'm getting extremely erratic behavior from my cells in trying to accomplish this. Initially, the first cell with an image will populate correctly. However, as you scroll through, images begin to size incorrectly and others don't appear at all.

My images are being downloaded asynchronously via Google Firebase.

I'm using Autolayout in my cell and have top, bottom, leading, and trailing constraints set properly for all subviews. The issue seems to be limited only to the ImageView. No other elements are affected.

I've tried setting a height constraint on the ImageView and programmatically resizing it depending on whether or not an image is meant to appear. This does not seem to be helping.

Here are my tableview delegate methods:

// Tableview methods
func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return mPosts!.count
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableView.automaticDimension
}

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return 600
}

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

    let post = mPosts![indexPath.row]

    cell.posterNameDisplay.text = post.getPosterName() + " " + (mAccount?.getFamilyName())!

    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "MMMM dd, yyyy @ hh:mm a"
    let dateString = dateFormatter.string(from: post.getTimeStamp())
    cell.timestampDisplay.text = dateString

    cell.postMessageDisplay.text = post.getPostMessage()

    AccountUtils.loadProfilePhoto(ProfileId: post.getPosterId(), ProfilePhoto: cell.profilePhoto)
    cell.profilePhoto.layer.borderWidth = 1.0
    cell.profilePhoto.layer.masksToBounds = false
    cell.profilePhoto.layer.borderColor = UIColor.white.cgColor
    cell.profilePhoto.layer.cornerRadius = cell.profilePhoto.frame.size.width / 2
    cell.profilePhoto.clipsToBounds = true

    PostUtils.loadPostImage(View: cell.postImage, PostId: post.getPostId())

    return cell
}

And here is the utility method I'm using to download and set the image in the ImageView:

public static func loadPostImage(View postImage: UIImageView, PostId postId: String) {
    let storage = Storage.storage()
    let storageRef = storage.reference()
    let photoRef = storageRef.child("photos/" + Auth.auth().currentUser!.uid + postId + ".jpg")

    photoRef.getData(maxSize: 1 * 1024 * 1024) { data, error in
        if error != nil {
            postImage.image = nil
        }

        else {
            let image = UIImage(data: data!)
            postImage.image = image
        }
    }
}

Solution

  • If your cells have more than one unique layout, each unique layout should receive its own reuse identifier (and better it's own subclass).

    Create two cell subclasses - one for text only, the second is for cell with image

    Change your func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell to something similar:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
        let isCellHasImage: Bool = // understand if it will image cell or just text cell
    
        if (isCellHasImage) {
            // dequeue and return your image cell
            // return cell
        }
    
        // dequeue and return your text-only cell
        // ...
        // return cell
    }