Dynamic height for a UITableView based on a dynamic collection view

荒凉一梦 提交于 2021-02-08 02:01:12

问题


I have to add a UICollectionView inside a UITableViewCell. The collectionView can have a different number of items. So the collectionView should adjust properly inside the tableView.

I have implemented this inside my Project: https://github.com/vishalwaka/DynamicCollectionViewInsideTableViewCell

In my case, the height of the cell is not being the adjusted after the height of the collectionView is set.

How can I set the collectionView to not be scrollable and also show all content inside the tableviewcell.


回答1:


Create a collectionView inside UITableViewCell with Leading, Trailing, Top, Bottom, Height constraint like this:

Now, in your tableViewCell class make a NSKeyValueObserver and add this on UICollectionView's contentSize property and assign contentSize changes to collectionView's height constraint.

class FormCollectionTableViewCell: UITableViewCell{

  var collectionViewObserver: NSKeyValueObservation?

  override func awakeFromNib() {
    super.awakeFromNib()
    addObserver()
  }

  override func layoutSubviews() {
        super.layoutSubviews()
        layoutIfNeeded()
  }

  func addObserver() {
       collectionViewObserver = collectionView.observe(\.contentSize, changeHandler: { [weak self] (collectionView, change) in
            self?.collectionView.invalidateIntrinsicContentSize()
            self?.collectionViewHrightConstarint.constant = collectionView.contentSize.height
            self?.layoutIfNeeded()
        })
    }
   deinit {
      collectionViewObserver = nil
   }
}

And in your viewController class use the following code:

override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        tableView.reloadData()
}

Now, your table view will have collection view with dynamic height. Don't forgot to disable scrolling of UICollectionView and adjusting it's flow.




回答2:


Create the following subclass of a collectionView:

class IntrinsicSizeCollectionView: UICollectionView {

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    self.setup()
}

override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
    super.init(frame: frame, collectionViewLayout: layout)

    self.setup()
}

override func layoutSubviews() {
    super.layoutSubviews()
    if !self.bounds.size.equalTo(self.intrinsicContentSize) {
        self.invalidateIntrinsicContentSize()
    }
}

override var intrinsicContentSize: CGSize {
    get {
        let intrinsicContentSize = self.contentSize
        return intrinsicContentSize
    }
}

func setup() {
    self.isScrollEnabled = false
    self.bounces = false
}

Add the collectionView to a tableViewCell and set its constraints leading, trailing, top, bottom to the bounds of the cell. Also set the intrinsic size as in the screenshot.

The size of the tableViewCell should set to the UITableViewAutomaticdimension and also an estimatedHeight for this cell should be set in the dataSource of the TableView.

That should be all.




回答3:


After checking your project I suggest you use a slightly different approach.

After updating the model, you can ask the collection view layout what the new content size will be (collectionViewLayout.collectionViewContentSize). You then update the height constraint constant.

So, you should:

  1. update your model
  2. reload the row
  3. ask the layout for its new contentSize
  4. update the height constraint constant

This modification in your project should achieve this:

// Remove the content size observing logic.

func configureForVehicle(_ vehicle: Vehicle, selectedHouseType: String) {
    self.vehicle = vehicle

    applianceCollectionView.reloadData()

    // Set the height constraint to the new value, found in the layout's content size info.
    // Since the model (self.vehicle) was already updated, the layout will return the new content size.
    // Because this is all happening while the cell is being reloaded, the cell will also resize properly.
    // You don't need to perform any other setNeedsLayout/layoutIfNeeded calls.
    self.applianceCollectionViewHeightConstraint.constant = applianceCollectionView.collectionViewLayout.collectionViewContentSize.height
}

Note the comment about not needing any additional layout update calls. Only calling tableView.reloadRows(at: [indexPath], with: .automatic) should be enough. Unnecessary layout updates can cause a performance hit.


As a side note there is another approach you can use if you wish to animate the height change without reloading the cell. In this case you would need to keep/find a reference to the cell you wish to update then do the following:

  1. Update the model for the cell without calling any table view reload calls.
  2. Reload the collection view on the updated cell.
  3. Update the height constraint same way as above.
  4. Use a callback of some sort to notify your table view that a cell wishes to update its height.
  5. In the callback call tableView.beginUpdates(); tableView.endUpdates(). This causes the tableView to re-layout its cells and animate height changes.

From beginUpdates() documentation:

You can also use this method followed by the endUpdates() method to animate the change in the row heights without reloading the cell. This group of methods must conclude with an invocation of endUpdates().



来源:https://stackoverflow.com/questions/54038361/dynamic-height-for-a-uitableview-based-on-a-dynamic-collection-view

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!