1

I've set up a UITableViewCell with UITableViewAutomaticDimension
The TableViewCell has a UICollectionView embedded in it which is not scrollable but can have a variable height based on the content of the collectionview.

Right now what I've tried is the render the cell and assign the height constraint of the collectionview to a variable collectionViewHeightConstraint and then update the height once the collectionview is rendered in the layoutSubviews method

override func layoutSubviews() {
    super.layoutSubviews()
    self.collectionViewHeightConstraint?.constant = self.collectionView!.contentSize.height
}

This is what the collectionview constraints look like (using cartography) :

    self.contentView.addSubview(self.collectionview)   
    self.contentView.addSubview(self.holdingView)
    constrain(self.holdingView!, self.contentView) {
        holderView, container in
        holderView.top == container.top
        holderView.left == container.left
        holderView.right == container.right
        holderView.bottom == container.bottom
    }
    constrain(self.collectionView!, self.holdingView) {
        collectionView, containerView in
        collectionView.top == containerView.top
        collectionView.left == containerView.left
        collectionView.right == containerView.right
        collectionViewHeightConstraint = collectionView.height == collectionViewHeight
        containerView.bottom == collectionView.bottom
    }

But that does not seem to update the cell height.

Is there any way to update the same?

Edit
This is not a duplicate question as suggested by some people and the explanation of why is in the comments below.

Aatish Molasi
  • 2,138
  • 3
  • 20
  • 43
  • Try reloading the `UITableView` after setting the height and checking if the `heightConstant != contentSize`. Use this to check if the height of the `UICollectionView` is updated properly. – Rikh Apr 06 '17 at 13:05
  • @Rikh : That sounds like a solution but also something very messy and hacky. I mean if nothing else, it's what I'll go with. – Aatish Molasi Apr 06 '17 at 13:17
  • Well, you do need to inform your `UITableView` to recalculate its height, you could always try reloading the particular row only if you know which collection view you updated (if you don't wish to reload the entire `UITableView`)! – Rikh Apr 06 '17 at 13:18
  • @Rikh it's still quite messy since layoutsubviews is being called multiple times and sometimes the content height is coming as 0 – Aatish Molasi Apr 06 '17 at 13:50
  • Possible duplicate of [Expand UILabel inside UITableView with "more" button like Instagram](http://stackoverflow.com/questions/43096231/expand-uilabel-inside-uitableview-with-more-button-like-instagram) – DonMag Apr 07 '17 at 11:40
  • @DonMag That question is different from this one as it only talks about expanding the cell based on the content heights which can be managed by simple reload of row/table with a boolean variable. In this case we will not be simply able to do that as we need to store the constraint *after* it is drawn and then update the row from the tableview. The solution is completely different from the duplicate question. Not to mention capturing the constraint is a task in itself. – Aatish Molasi Apr 09 '17 at 08:49
  • @AatishMolasi - perhaps a little more of your actual code... I've done quite a few examples of triggering the auto-layout update *without* needing to call `.reloadData`. Including async downloading of images, where the cell height is not known until the image download completes (which sounds a bit like your "don't know the height of the collection view until..." case). – DonMag Apr 09 '17 at 14:24
  • @DonMag It works okay for images, but try adding a collectionview inside the cell (from my experience it won't behave the same way). I've added the constraints if that would help – Aatish Molasi Apr 10 '17 at 06:19
  • @AatishMolasi - I added an example of using a "dynamic" collection view inside a tableview cell, and updating the dynamic tableview row heights *without* calling `.reloadData`... take a look at "Example D" in this repo (if you are interested): https://github.com/DonMag/DynamicCellHeight – DonMag Apr 10 '17 at 15:04
  • let me branch this to see if I can recreate my issue – Aatish Molasi Apr 11 '17 at 17:54

3 Answers3

2

Since the comment was too small a space, I'll put everything here:

Note: You don't actually have to set the height constraint in viewDidLayoutSubviews just somewhere you can be sure that the UICollectionView has been set and your layout has been setup properly on your whole screen! For example, doing it in viewDidAppear and then calling layoutIfNeeded() will also work. Moving it into viewDidAppear will only work if you have your UICollectionView setup before viewDidAppear is called i.e you know your UICollectionView dataSource beforehand.

Fix 1:

Try reloading the UITableView after setting the height and checking if the heightConstant != contentSize. Use this to check if the height of the UICollectionView is updated properly i.e.:

override func layoutSubviews() {
    super.layoutSubviews()

    if self.collectionViewHeightConstraint?.constant != self.collectionView!.contentSize.height{

        self.collectionViewHeightConstraint?.constant = self.collectionView!.contentSize.height

        //to make sure height is recalculated
        tableView.reloadData()
        //or reload just the row depending on use case and if you know the index of the row to reload :)
    }
}

I agree with your comment and that it is messy, I meant use that as a fix and/or to check if that is where the problem lies actually!

As for why it is 0, that happens probably because your UICollectionView hasn't been set yet (cellForItem hasn't been called yet) so contentSize isn't actually calculated!

Fix 2:

Once your dataSource for the UICollectionView has been set, that is you receive the data, you calculate the height the UICollectionView contentSize will have manually and set it once and reload the row. If the calculation is a tedious task, just set the dataSource and call reloadData on UICollectionView. This will ensure the UICollectionView is setup properly and then set the constraint of the cell to be the contentSize and call reloadData or reloadRow on the UITableView.

You basically can set the heightConstraint anytime after your UICollectionView has been setup and your view has been laid out. You just need to called tableView.reloadData() afterwards.

Rikh
  • 4,078
  • 3
  • 15
  • 35
  • You don't have to call `tableView.reloadData()` to make sure the height is recalculated, `tableView.beginUpdates(); tableView.endUpdates()` should be enough – crizzis Apr 06 '17 at 18:42
  • @crizzis I realised that in the post below :). But to be honest, I've found `reloadData()` to always give the needed result as opposed to `beginUpdates()` (maybe I just don't do it right D:) and you don't always want the animation provided. – Rikh Apr 07 '17 at 04:29
  • @Rikh Could you update the answer to update a single cell or use a delegate to call the tableview object with that cell index to update that cell rather than reloda the whole table for every cell – Aatish Molasi Apr 07 '17 at 09:33
0

You can reload particular cell of tableview

let indexPath = IndexPath(item: rowNumber, section: 0)
    tableView.reloadRows(at: [indexPath], with: .top)
Vinod Kumar
  • 3,375
  • 1
  • 17
  • 35
0

Going by your requirement, I guess if we load the collectionview first and then load the tableview with the correct height of the collectionview, we can solve this.

collectionView.reloadData()
        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            self.collectionViewHeightConstraint?.constant = self.collectionView!.contentSize.height
        })
tableview.reloadData()

By this when tableview loads the cell has the desired height based on the collection view content size.

Rakshith Nandish
  • 599
  • 3
  • 13
  • The collectionViewHeightConstraint is in the cell. Are you asking me to reload the tableview from inside the cell? Besides this will reinitiate the whole rendering process resetting the constraint values – Aatish Molasi Apr 06 '17 at 13:46
  • The call to reload the collectionview should be in the tableviews cellForRowAtIndexPath right? how about you add that code inside the cell, once it completes, you should have the desired height. Also I would suggest ensuring the code above runs only once, else it can slow down the app as cellForRowAtIndexPath is called many times – Rakshith Nandish Apr 06 '17 at 14:12