问题
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:
- update your model
- reload the row
- ask the layout for its new
contentSize
- 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:
- Update the model for the cell without calling any table view reload calls.
- Reload the collection view on the updated cell.
- Update the height constraint same way as above.
- Use a callback of some sort to notify your table view that a cell wishes to update its height.
- 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