我不知道为什么设计能适应其内容的单元格如此复杂。它不需要那么多代码,我仍然不明白为什么UIKit
无法正确处理此问题。
无论如何,这是我的问题(我已编辑了整个帖子):
我有一个UICollectionViewCell
,其中包含一个UITableView
。
这是我的sizeForItem
方法:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
var cellWidth: CGFloat = collectionView.bounds.size.width
var cellHeight: CGFloat = 0
let cellConfigurator = items[indexPath.item].cellConfigurator
if type(of: cellConfigurator).reuseId == "MoonCollectionViewCell" {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: type(of: cellConfigurator).reuseId, for: indexPath) as? MoonCollectionViewCell {
cell.contentView.layoutIfNeeded()
let size = cell.selfSizedTableView.intrinsicContentSize
cellHeight = size.height
}
}
return CGSize.init(width: cellWidth, height: cellHeight)
}
sizeForItem
在cellForItem
之前被调用,这就是layoutIfNeeded
的原因,因为我无法获得正确的intrinsic content size
。
我已按照建议删除了XIB,并在情节提要中设计了UICollectionViewCell。
这是我在Storyboard中设计的UICollectionViewCell(仅在XIB文件中设计了UITableViewCell)
我只在UICollectionViewCell中添加了一个UITableView。
我希望UICollectionViewCell
根据tableView
的高度调整其大小。
现在这是我的tableView
:
我已经从post创建了UITableView
的子类
class SelfSizedTableView: UITableView {
var maxHeight: CGFloat = UIScreen.main.bounds.size.height
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
self.layoutIfNeeded()
}
override var intrinsicContentSize: CGSize {
let height = min(contentSize.height, maxHeight)
return CGSize(width: contentSize.width, height: height)
}
}
请注意,我有disabled scrolling
,我有dynamic prototype
用于tableView单元格,样式为grouped
。
编辑::检查配置方法,它来自我用来以通用方式配置所有UICollectionViewCell
func configure(data: [MoonImages]) {
selfSizedTableView.register(UINib.init(nibName: "MoonTableViewCell", bundle: nil), forCellReuseIdentifier: "MoonTableViewCell")
selfSizedTableView.delegate = self
selfSizedTableView.dataSource = moonDataSource
var frame = CGRect.zero
frame.size.height = .leastNormalMagnitude
selfSizedTableView.tableHeaderView = UIView(frame: frame)
selfSizedTableView.tableFooterView = UIView(frame: frame)
selfSizedTableView.maxHeight = 240.0
selfSizedTableView.estimatedRowHeight = 40.0
selfSizedTableView.rowHeight = UITableView.automaticDimension
moonDataSource.data.addAndNotify(observer: self) { [weak self] in
self?.selfSizedTableView.reloadData()
}
moonDataSource.data.value = data
}
仅供参考 dataSource是具有动态值(泛型)和观察者模式的自定义dataSource,用于在设置数据后重新加载collection / tableView。 >
启动应用程序时,我也会收到此警告。
[CollectionView]检测到尝试更新布局信息 虽然已经在计算布局(即可重入) 呼叫)。这将导致意外行为或崩溃。这可能 如果在调用委托时触发了布局传递,则会发生这种情况。
有关如何处理此问题的任何提示或建议吗?
因为我面临一个奇怪的行为,就像我的sizeForItem使用随机值。 UICollectionViewCell
的高度与我的UITableView
内部内容大小的高度不同。
如果我的UITableView
中有2行,则UICollectionView
并不总是等于此大小。我真的不知道该怎么实现...
我应该invalideLayout
吗?
答案 0 :(得分:2)
也许这不是您想要的答案,但这是我的两分钱。对于您的特定要求,更好的解决方案是远离UITableView
,而使用UIStackView
或您的自定义容器视图。
这是为什么:
UITableView
是UIScrollView
的子类,但是由于您已禁用其滚动功能,因此不需要UIScrollView
。UITableView
主要用于重用单元格,以提高性能并使代码更结构化。但是,由于您要使其内容大小一样大,因此不会重复使用任何单元格,因此UITableView
的功能不会得到任何利用。因此,实际上您不需要,您不应该在UITableView
内使用UIScrollView
或UICollectionViewCell
来满足您的要求。
如果您同意以上部分,则可以从我们的实践中学到一些东西:
UIView
的自定义视图中,而不是直接放入UITableViewCell
或UICollectionViewCell
。然后将其添加到UITableViewCell
或UICollectionViewCell
的{{1}}和设置约束中。通过这种结构,我们可以在更多场景中重用我们的自定义视图。contentView
创建“单元”的“行”,将它们添加到垂直UITableView
中,创建约束确定UIStackView
的宽度。自动布局将处理其余的事情。UIStackView
一起使用时,要计算所需的高度,可以在单元格的UICollectionViewCell
函数内部,使用preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes)
计算高度,进行一些检查并返回。另外,请记住,contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
的宽度更改时会使布局无效。答案 1 :(得分:1)
我试图在发布的代码中找到罪魁祸首,但是似乎有很多活动的部分。因此,我将尝试给出一些提示,希望能有所帮助。
理论上(iOS 12有一些警告),自行设置UICollectionViewCells的大小应该并不困难。您基本上可以将collectionViewLayout.estimedItemSize
设置为任何值(以下是首选常量),如下所示:
(collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
然后,您必须确保以可自行调整大小的方式设置单元格中的约束。即自动布局可以计算单元格的宽度和高度。您正在提供tableView的intrinsicContentSize
,它由其所有四个端的超级视图包裹,因此应该可以。
如上所述,一旦设置了estimatedItemSize
,就不应实现返回大小的委托方法:
func collectionView(_: UICollectionView, layout: UICollectionViewLayout, sizeForItemAt: IndexPath) -> CGSize
可以在此处找到快速教程以作进一步参考:https://medium.com/@wasinwiwongsak/uicollectionview-with-autosizing-cell-using-autolayout-in-ios-9-10-84ab5cdf35a2
正如我在理论上说的那样,这并不难,但是在iOS 12上自动调整单元格大小似乎不起作用,请参见此处In iOS 12, when does the UICollectionView layout cells, use autolayout in nib
如果我处于您的位置,我将从头开始,逐步增加复杂性:
intrinsicContentSize
的覆盖来实现自定义单元格;可能是通过使用iOS 11.4 SDK排除与iOS 12有关的问题(最简单的方法是下载最新的Xcode 9并从那里工作);如果不可能,请在此步骤中修复iOS 12 希望这会有所帮助。
顺便说一句,控制台中的警告可能是由于在委托方法中调用layoutIfNeeded()
引起的。它会触发立即进行布局遍历,而UICollectionView
会在收集所有尺寸后完成。
答案 2 :(得分:0)
这确实很棘手,但是我找到了解决此问题的可行方法。据我所知,我是从聊天应用程序获得的,该应用程序的消息气泡大小是动态的。
我们在这里:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
// Minimum size
let frame = CGRect(x: 0, y: 0, width: view.frame.width - 30, height: 0)
let cell = MoonCollectionViewCell()
// Fill it with the content it will have in the actual cell,
// cell.content is just an example
let cell.content = items[indexPath.item]
cell.layoutIfNeeded()
// Define the maximum size it can be
let targetSize = CGSize(width: view.frame.width - 30, height: 240)
let estimatedSize = cell.systemLayoutSizeFittingSize(tagetSize)
return CGSize(width: view.frame.width - 30, height: estimatedSize.height)
}
它的基本作用是定义最小帧和目标尺寸。然后,通过调用systemLayoutSizeFittingSize,它将单元格的大小调整为最佳大小,但不大于targetSize。
根据您的需要调整代码,但这应该可行。