将UICollectionView嵌套到UITableViewCell中 [英] Nest UICollectionView into UITableViewCell

查看:148
本文介绍了将UICollectionView嵌套到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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆