将UICollectionView嵌套到UITableViewCell中 [英] Nest UICollectionView into UITableViewCell
问题描述
我正在尝试建造一些东西
我正在尝试使用UICollectionView构建标签列表视图,并将其嵌套到我的自定义UITableViewCell中.
我现在有什么
搜索互联网后,我找到了问题的关键:
- 子类UICollectionView并实现其固有的内容大小属性.
但是,当我将自定义UICollectionView嵌套到一个自定义大小的UITableViewCell中时,整个过程运行不正常.布局已损坏.
无论我如何更改代码,我都会获得以下3个越野车用户界面之一.
集合视图的高度总是错误的,无论是太小还是太大,它都不能正确地容纳其内容.
当我使用 Debug View Hierarchy
来检查视图时,我发现尽管UI损坏了,但集合视图的 contentSize
属性具有正确的值.似乎内容大小属性无法及时反映到UI.
class IntrinsicCollectionView:UICollectionView {覆盖var contentSize:CGSize {didSet {invalidateIntrinsicContentSize()}}覆盖var internalContentSize:CGSize {layoutIfNeeded()返回CGSize(width:UIView.noIntrinsicMetric,height:collectionViewLayout.collectionViewContentSize.height)}覆盖init(框架:CGRect,collectionViewLayout布局:UICollectionViewLayout){super.init(框架:框架,collectionViewLayout:布局)isScrollEnabled =假}需要初始化吗?(编码器:NSCoder){fatalError(尚未实现的init(coder :)")}}
关于如何创建具有固有内容大小的自定义UICollectionView的解决方案很多.其中一些可以正常工作.但是,当将它们嵌套到UITableViewCell中时,它们都不起作用.
对于仅将一个UICollectionView嵌套到UITableViewCell中而不使用其他视图也有一些答案.但是,如果UITableViewCell中也有一些UILabel,它将无法正常工作.
我将所有代码上传到github.
并且我在调试控制台中看到了这一点:
collView宽度:66.0固有高度:350.0collView宽度:343.0内部高度:30.0
告诉我的是,在收集视图具有完整框架之前,会要求其收集 intrinsicContentSize
.
此时,它会填充其单元格,其布局最终以 350
的 .collectionViewContentSize.height
(此行具有六个标签"单元格)结束).
然后自动布局将执行另一遍操作...收集视图现在具有有效的帧宽度(基于单元格宽度)...并且单元格已重新布置.
不幸的是,表格视图已经 已经 基于初始集合视图 intrinsicContentSize.height
设置了行高./p>
因此,有两个步骤可能(应该)解决此问题:
在 ListViewCell
中,获取标签时使集合视图的内容大小无效:
func setTags(_标签:[String]){self.tags =标签collectionView.reloadData()//添加此行collectionView.invalidateIntrinsicContentSize()}
然后,在 ListViewController
中,我们需要在框架更改后重新加载表:
//添加此变量var currentWidth:CGFloat = 0//实现viewDidLayoutSubviews()覆盖func viewDidLayoutSubviews(){super.viewDidLayoutSubviews()如果view.frame.width!= currentWidth {currentWidth = view.frame.widthtableView.reloadData()}}
(通过非常快速的测试)似乎可以给我可靠的结果:
并在设备旋转时:
I'm trying to build something
I'm trying to build a tag list view using UICollectionView and nest it into my custom UITableViewCell.
What do I have now
After searching the internet, I find the key to the problem:
- Subclass UICollectionView and implement it's intrinsic content size property.
However, when I nest my custom UICollectionView into a self-sizing UITableViewCell, the whole thing doesn't work well. The layout is broken.
No matter how do I change the code, I get one of the following 3 buggy UIs.
The height of the collection view is always wrong, either too small or too large, it can not hug it's content just right.
When I use Debug View Hierarchy
to check the views, I find that although the UI is broken, the contentSize
property of the collection view has a correct value. It seems that the content size property can not be reflected to the UI in time.
class IntrinsicCollectionView: UICollectionView {
override var contentSize: CGSize {
didSet {
invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
layoutIfNeeded()
return CGSize(width: UIView.noIntrinsicMetric, height: collectionViewLayout.collectionViewContentSize.height)
}
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
isScrollEnabled = false
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
There are many solution about how to create a custom UICollectionView with intrinsic content size. Some of them can work correctly. But when nesting them into a UITableViewCell, none of them works well.
There are also some answer about just nest one UICollectionView into UITableViewCell without other views. But if there are also some UILabel in UITableViewCell, it won't work.
I upload all the code to github. https://github.com/yunhao/nest-collectionview-in-tableviewcell
Thank you!
I'll try to explain what's going on....
To make it easy to understand, in your ListViewController
let's work with just one row to begin with:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1 // items.count
}
In your ListViewCell
class, add these lines at the end of prepareViews()
:
// so we can see the element frames
titleLabel.backgroundColor = .green
subtitleLabel.backgroundColor = .cyan
collectionView.backgroundColor = .yellow
In your IntrinsicCollectionView
class, let's add a print()
statement to give us some information:
override var intrinsicContentSize: CGSize {
layoutIfNeeded()
// add this line
print("collView Width:", frame.width, "intrinsic height:", collectionViewLayout.collectionViewContentSize.height)
return CGSize(width: UIView.noIntrinsicMetric, height: collectionViewLayout.collectionViewContentSize.height)
}
When I then run the app on an iPhone 8, I get this result:
and I see this in the debug console:
collView Width: 66.0 intrinsic height: 350.0
collView Width: 343.0 intrinsic height: 30.0
What that tells me is that the collection view is asked for its intrinsicContentSize
before it has a complete frame.
At that point, it fills in its cells, and its layout ends up with a .collectionViewContentSize.height
of 350
(this row has six "tags" cells).
Auto-layout then performs another pass... the collection view now has a valid frame width (based on the cell width)... and the cells are re-laid-out.
Unfortunately, the table view has already set the row height(s), based on the initial collection view intrinsicContentSize.height
.
So, two steps that may (should) fix this:
In ListViewCell
, invalidate the content size of the collection view when you get the tags:
func setTags(_ tags: [String]) {
self.tags = tags
collectionView.reloadData()
// add this line
collectionView.invalidateIntrinsicContentSize()
}
Then, in ListViewController
, we need to reload the table after its frame has changed:
// add this var
var currentWidth: CGFloat = 0
// implement viewDidLayoutSubviews()
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if view.frame.width != currentWidth {
currentWidth = view.frame.width
tableView.reloadData()
}
}
That seems (with very quick testing) to give me reliable results:
and on device rotation:
这篇关于将UICollectionView嵌套到UITableViewCell中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!