简体   繁体   中英

UITableViewCell dynamic row height + custom UIView + Auto Layout

I am trying to get a custom photo container with multiple UIImageViews to fit in my tableview cell. The view contains a variable number of images (1 ~ 9), and its height would change correspondingly from 1x to 3x imageHeight.

I used AutoLayout to define the top/bottom/leading/trailing margins with the tableview and the custom UIView inside, and to enable self-sizing cells, I have set

tableView.estimatedRowHeight = X
tableView.rowHeight = UITableViewAutomaticDimension

I initialize these cells with

tableView.register(nib: forCellReuseIdentifier:)

and in tableView(_ tableView: cellForRowAt:) method, I setup the cell with:

let cell = tableView.dequeueReusableCell(
    withIdentifier: "test9cell", 
               for: indexPath) as! SocialFeedTableViewCell
cell.photoContainer.setup(with: urls)
cell.photoContainer.loadImages()
return cell 

where setup() hooks each imageView in the container with a URL

func setup(with urls: [URL]) {
    self.imageUrls = urls
    for i in 0 ..< urls.count {
        let imageView = UIImageView(frame: CGRect.zero)
        self.addSubview(imageView)
        self.imageViews.append(imageView)
    }
    self.setNeedsLayout()
}

func loadImages() {
    self.imageViews.forEach { imageView in
        imageView.frame = // Calculate position for each subview
        imageView.sd_setImage(...) // Load web image asynchronously
    }
}

Defining intrinsicContentSize for the view:

override var intrinsicContentSize {
    let frameWidth = self.frame.size.width
    var frameHeight: CGFloat
    switch self.imageUrls.count { // range from 1...9
    case 1...3:
        frameHeight = frameWidth / 3
    case 4...6:
        frameHeight = frameWidth / 3 * 2
    default:
        frameHeight = frameWidth
    return CGSize(frameWidth, frameHeight)
}

override func layoutSubviews() {
    super.layoutSubviews()
    self.imageViews.forEach { imageView in
        imageView.frame = // Calculate position for each subview
    }
}

The problem here is: after I set the initial intrinsicContentSize, the container's frame size changes in layoutSubviews() afterwards. Although by then I can position the imageView subviews correctly, the cell height will not be changed anymore.

Hope I am not making this problem more confusing. Could someone point out how would I resize the cell height AFTER modifying the contents of its UIView subview? Thanks!

There are a few things here that will be causing you some issues with dynamically sized cells.

  1. You are adding multiple items to the cell but are not defining any auto layout constraints on the image views, so it does not know how to properly place / stack the items.
  2. from the code above you are adding UIImageView with a frame of CGRectZero, without autulayout rules they will either stay zero or try to adjust to the contentsize when you add an image, but they wont adjust the cells height.
  3. If you are loading images from the network they will likely be added/rendered after the tableviewcell has done its initial rendering. So you will likely need to cache the loaded images and reload the cell so that they can load in at the right time.

Now this last point is alot more complicated.

Dynamic UITableViewCell's calculate their height based on the autolayout rules of the content within them. You MUST have enough constraints from your content to the UITableViewCell's contentView property (to all edges) that give the cell enough information to place each item, calculate its overall height and width and therefore it is able to calculate the new height.

Using just one image isn't too bad for dynamic sized cells. but placing multiple items dynamically without any rules after the cell has initially rendered will not work.

You need to decide how these images should be laid out in your cell. once you have this you can look at adding the required constraints as you add the image views. Personally I would only add the image views once i have received each image.

Previously when I have done this I have cached the images once received and re-loaded the cell so that the image can be placed in the cell as it is rendered, allowing the tableview cell to calculate its height based off the image dimensions and constraints.

You may want to also consider using a collection view or merging the images together into a single image and using that in your cell. You could do this on device at runtime or server side if you have that kind of access to the images.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM