使用 UITableViewAutomaticDimension 制作带有嵌入式 UICollectionView 的 UITableView
Posted
技术标签:
【中文标题】使用 UITableViewAutomaticDimension 制作带有嵌入式 UICollectionView 的 UITableView【英文标题】:Making UITableView with embedded UICollectionView using UITableViewAutomaticDimension 【发布时间】:2018-01-04 10:46:32 【问题描述】:我想创建一个带有标题标签的UITableView
和嵌入的UICollectionView
(知道它的大小)以及一些使用UITableViewAutomaticDimension
的图标。问题是UITableView
在我有UICollectionView
时在计算单元格高度时遇到问题。我必须滚动 UITableView
才能重新计算大小。但即便如此,它也存在高度问题(如果从较大的高度重复使用它太大了)。最重要的是UICollectionView
中的图标从乞讨中不知道,但它们旨在从服务器加载。
我还尝试为UICollectionView
创建一个高度约束,但这样我会得到“无法同时满足约束”,这是由于与UIView-Encapsulated-Layout-Height
冲突而导致的,而且我自己的约束无论如何都被删除了。
我已经创建了一个带有示例项目的 GitHub 存储库(我做得尽可能简单):
https://github.com/piotrros/CollectionViewInTableView
【问题讨论】:
您的要求是什么?你想让集合视图滚动吗?或者您希望根据集合视图增加 tableview 高度? 您的要求是什么?你想让集合视图滚动吗?或者您希望根据集合视图增加 tableview 高度? 我希望每个集合视图的 tableView 高度增加。 @Makalele 分叉了项目,修复了它,创建了一个拉取请求,并更新了答案。编码愉快! 我认为使用自定义布局创建collectionView比放置这种内存效率低的解决方案更容易。 【参考方案1】:由于您在 cellForRow
中做了很多事情,因此需要时间准备,因此当您滚动时它无法正确显示
检查以下事项。
ViewController.swift
在视图中加载了
添加tableView.rowHeight = UITableViewAutomaticDimension
并替换此方法
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let foo = foos[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FooTableViewCell
cell.titleLabel.text = foo.title
cell.descriptionLabel.text = foo.description
self.view.layoutIfNeeded()
return cell
FooTableViewCell.swift
class FooTableViewCell: UITableViewCell
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var stackView: UIStackView!
@IBOutlet weak var descriptionLabel: UILabel!
@IBOutlet weak var iconsCollectionView: IconsCollectionView!
@IBOutlet weak var const_Height_CollectionView: NSLayoutConstraint!
override func awakeFromNib()
iconsCollectionView.translatesAutoresizingMaskIntoConstraints = false
iconsCollectionView.initFlowLayout(superviewWidth: self.frame.width)
iconsCollectionView.loadIconsSync()
iconsCollectionView.setNeedsLayout()
我已经删除了你的故事板的 StackView 表单,只给出了前导、尾随、顶部和底部约束(没什么复杂的)
这是输出
希望对你有帮助
编辑/更新
您的演示项目中有很多问题。 我对你的演示项目做了很多改动。
复制 XML
并将其粘贴到情节提要中。
别忘了连接高度约束
这里是完整的故事板XML
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="ios.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="CollectionViewInTableView" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" />
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="196" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="ZhZ-if-Yia">
<rect key="frame" x="0.0" y="0.0" />
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="cell" rowHeight="196" id="1WO-2S-MI9" customClass="FooTableViewCell" customModule="CollectionViewInTableView" customModuleProvider="target">
<rect key="frame" x="0.0" y="28" />
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="1WO-2S-MI9" id="2lF-aM-Z2U">
<rect key="frame" x="0.0" y="0.0" />
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vs4-91-woo">
<rect key="frame" x="8" y="8" />
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Description" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lD2-bR-lnm">
<rect key="frame" x="8" y="36.5" />
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="6sQ-gf-Y6x" customClass="IconsCollectionView" customModule="CollectionViewInTableView" customModuleProvider="target">
<rect key="frame" x="8" y="65" />
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" priority="750" constant="88" id="f9g-Du-pYg"/>
</constraints>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="Nts-Lf-FPD">
<size key="itemSize" />
<size key="headerReferenceSize" />
<size key="footerReferenceSize" />
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="item" id="8gX-Q1-0jG" customClass="BarCollectionViewCell" customModule="CollectionViewInTableView" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" />
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
<rect key="frame" x="0.0" y="0.0" />
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="6cf-uD-BQl">
<rect key="frame" x="0.0" y="0.0" />
</imageView>
</subviews>
</view>
<constraints>
<constraint firstAttribute="bottom" secondItem="6cf-uD-BQl" secondAttribute="bottom" id="0c3-ug-8tN"/>
<constraint firstItem="6cf-uD-BQl" firstAttribute="top" secondItem="8gX-Q1-0jG" secondAttribute="top" id="9xW-dN-c0m"/>
<constraint firstItem="6cf-uD-BQl" firstAttribute="leading" secondItem="8gX-Q1-0jG" secondAttribute="leading" id="dT6-RU-eE4"/>
<constraint firstAttribute="trailing" secondItem="6cf-uD-BQl" secondAttribute="trailing" id="nnz-oA-GgP"/>
</constraints>
<connections>
<outlet property="iconImageView" destination="6cf-uD-BQl" id="lFo-SS-Ego"/>
</connections>
</collectionViewCell>
</cells>
</collectionView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="right" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="sXa-Mn-xxW">
<rect key="frame" x="8" y="165.5" />
<state key="normal" title="Button"/>
</button>
</subviews>
<constraints>
<constraint firstItem="sXa-Mn-xxW" firstAttribute="leading" secondItem="2lF-aM-Z2U" secondAttribute="leading" constant="8" id="4ix-8u-0lO"/>
<constraint firstAttribute="bottom" secondItem="sXa-Mn-xxW" secondAttribute="bottom" id="50m-Bv-FF8"/>
<constraint firstAttribute="trailing" secondItem="sXa-Mn-xxW" secondAttribute="trailing" constant="8" id="8UW-vI-hge"/>
<constraint firstItem="6sQ-gf-Y6x" firstAttribute="leading" secondItem="2lF-aM-Z2U" secondAttribute="leading" constant="8" id="9ht-Ez-lJX"/>
<constraint firstAttribute="trailing" secondItem="vs4-91-woo" secondAttribute="trailing" constant="8" id="NOH-if-o7C"/>
<constraint firstItem="lD2-bR-lnm" firstAttribute="leading" secondItem="2lF-aM-Z2U" secondAttribute="leading" constant="8" id="S2D-Kj-5Og"/>
<constraint firstItem="vs4-91-woo" firstAttribute="leading" secondItem="2lF-aM-Z2U" secondAttribute="leading" constant="8" id="atk-7U-Mrw"/>
<constraint firstAttribute="trailing" secondItem="6sQ-gf-Y6x" secondAttribute="trailing" constant="8" id="bfY-uh-Su2"/>
<constraint firstItem="vs4-91-woo" firstAttribute="top" secondItem="2lF-aM-Z2U" secondAttribute="top" constant="8" id="gYO-XW-lmk"/>
<constraint firstAttribute="trailing" secondItem="lD2-bR-lnm" secondAttribute="trailing" constant="8" id="pkH-Pf-xE1"/>
<constraint firstItem="sXa-Mn-xxW" firstAttribute="top" secondItem="6sQ-gf-Y6x" secondAttribute="bottom" constant="8" id="w2O-4g-q6B"/>
<constraint firstItem="6sQ-gf-Y6x" firstAttribute="top" secondItem="lD2-bR-lnm" secondAttribute="bottom" constant="8" id="xky-sw-IcM"/>
<constraint firstItem="lD2-bR-lnm" firstAttribute="top" secondItem="vs4-91-woo" secondAttribute="bottom" constant="8" id="yG3-dE-CjF"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="const_Height_CollectionView" destination="f9g-Du-pYg" id="gw7-9T-hiU"/>
<outlet property="descriptionLabel" destination="lD2-bR-lnm" id="M4K-k5-6LN"/>
<outlet property="iconsCollectionView" destination="6sQ-gf-Y6x" id="FO2-dP-VNH"/>
<outlet property="titleLabel" destination="vs4-91-woo" id="HHy-1V-bTW"/>
</connections>
</tableViewCell>
</prototypes>
</tableView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="ZhZ-if-Yia" firstAttribute="top" secondItem="8bC-Xf-vdC" secondAttribute="top" id="J8c-wQ-FxB"/>
<constraint firstItem="ZhZ-if-Yia" firstAttribute="bottom" secondItem="6Tk-OE-BBY" secondAttribute="bottom" id="KCX-nj-zXy"/>
<constraint firstItem="ZhZ-if-Yia" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="QMU-w2-uUY"/>
<constraint firstItem="ZhZ-if-Yia" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="yx7-yg-aqC"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
<connections>
<outlet property="tableView" destination="ZhZ-if-Yia" id="WdR-nu-gjc"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="117.59999999999999" y="118.29085457271366"/>
</scene>
</scenes>
</document>
FOOTableviewCell.swift
protocol TableViewDelegate
func cellTapped (for:FooTableViewCell)
class FooTableViewCell: UITableViewCell
static let singleCellHeight = 88;
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var descriptionLabel: UILabel!
@IBOutlet weak var iconsCollectionView: IconsCollectionView!
@IBOutlet weak var const_Height_CollectionView: NSLayoutConstraint!
var delegateCollection : TableViewDelegate?
var bars:[Bar] = []
didSet
self.iconsCollectionView.reloadData()
iconsCollectionView.setNeedsLayout()
self.layoutIfNeeded()
const_Height_CollectionView.constant = iconsCollectionView.contentSize.height
self.layoutIfNeeded()
override func awakeFromNib()
iconsCollectionView.translatesAutoresizingMaskIntoConstraints = false
iconsCollectionView.initFlowLayout(superviewWidth: self.frame.width)
iconsCollectionView.setNeedsLayout()
iconsCollectionView.dataSource = self
iconsCollectionView.delegate = self
const_Height_CollectionView.constant = iconsCollectionView.contentSize.height
self.layoutIfNeeded()
self.setNeedsLayout()
func cellTapped ()
iconsCollectionView.setNeedsLayout()
self.layoutIfNeeded()
self.setNeedsLayout()
const_Height_CollectionView.constant = iconsCollectionView.contentSize.height
self.delegateCollection?.cellTapped(for: self)
extension FooTableViewCell : UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return bars.count + 1
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "item", for: indexPath) as! BarCollectionViewCell
let row = indexPath.row
if(row >= bars.count)
cell.iconImageView.image = UIImage(named: "add.png")
return cell
else
let bar = bars[row]
cell.iconImageView.image = UIImage(named: bar.imageName)
print(bar.imageName)
return cell
extension FooTableViewCell : UICollectionViewDelegate
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
//deselectItem(at: indexPath, animated: true)
if(indexPath.row >= bars.count) //it's a plus button
self.delegateCollection?.cellTapped(for: self)
IconCollectionView.swift
import UIKit
class IconsCollectionView: DynamicCollectionView
var columnLayout:ColumnFlowLayout?
override func awakeFromNib()
func initFlowLayout(superviewWidth:CGFloat)
let layout = ColumnFlowLayout(
cellsPerRow: 4,
superviewWidth: superviewWidth,
minimumInteritemSpacing: 0,
minimumLineSpacing: 0,
sectionInset: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
)
columnLayout = layout
collectionViewLayout = layout
ViewController.Swift
import UIKit
class ViewController: UIViewController
@IBOutlet weak var tableView: UITableView!
var foos:[Foo] = []
var bars:[[Bar]] = [[]]
override func viewDidLoad()
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
tableView.estimatedRowHeight = 188
tableView.rowHeight = UITableViewAutomaticDimension
for i in stride(from: 1, to: 10, by: 1)
let foo = Foo()
foo.title = "Item \(i)"
foo.description = "Description \(i)"
foos.append(foo)
bars.removeAll()
for _ in 0 ..< foos.count
bars.append(self.loadIconsSync())
func loadIconsSync() -> [Bar]
var barObjects :[Bar] = []
let iconsCount = Utils.rnd(3, 8)
for _ in stride(from: 1, to: iconsCount, by: 1)
barObjects.append(self.getRandomItem())
return barObjects
func getRandomItem() -> Bar
let randomIndex = Utils.rnd(1, 10)
let bar = Bar()
bar.imageName = "icon_\(randomIndex).png"
return bar
extension ViewController : UITableViewDataSource,UITableViewDelegate
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let foo = foos[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FooTableViewCell
var bar = bars[indexPath.row]
cell.bars = bar
cell.titleLabel.text = foo.title
cell.descriptionLabel.text = foo.description
cell.delegateCollection = self
self.view.layoutIfNeeded()
cell.const_Height_CollectionView.constant = cell.iconsCollectionView.contentSize.height
self.view.layoutIfNeeded()
return cell
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return foos.count
extension ViewController : TableViewDelegate
func cellTapped(for obj: FooTableViewCell)
if let indexPath = tableView.indexPath(for: obj)
bars[indexPath.row].append(getRandomItem())
self.tableView.beginUpdates()
self.tableView.reloadRows(at: [indexPath], with: .automatic)
self.tableView.endUpdates()
输出
编辑/更新 2
我不知道方向支持和 ipad 支持。
现在,当方向改变时,我们必须重新布局集合视图。
所以逻辑是
项目总数 + 1(+ 1 因为那个加号图标)
项目大小 *(项目总数 / 3).rounded
假设你有 7 个项目
所以项目大小为 93(每行)* ( 8 / 3).rounded = 279
因此,您需要根据 iPad 和横向模式的要求管理一些硬编码值。 现在我正在考虑每行 3 个对象,与 iPhone 设计相同。
这里硬编码的单元格数是3,你可以自己管理。
第一步:
在 viewContorller.swift 中添加 Follow 方法
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
super.viewWillTransition(to: size, with: coordinator)
self.tableView.beginUpdates()
self.tableView.reloadData()
self.tableView.endUpdates()
替换 cellForRowAtIndexPath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let foo = foos[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FooTableViewCell
cell.iconsCollectionView.initFlowLayout(superviewWidth: self.tableView.frame.width)
let bar = bars[indexPath.row]
cell.bars = bar
cell.titleLabel.text = foo.title
cell.descriptionLabel.text = foo.description
cell.delegateCollection = self
self.view.layoutIfNeeded()
let items:CGFloat = CGFloat(bar.count + 1)
let value = (items / 3.0).rounded(.awayFromZero)
cell.const_Height_CollectionView.constant = CGFloat((cell.iconsCollectionView.collectionViewLayout as! UICollectionViewFlowLayout).itemSize.height * value)
self.view.layoutIfNeeded()
cell.iconsCollectionView.setNeedsLayout()
return cell
还有
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
if let footCell = cell as? FooTableViewCell
footCell.const_Height_CollectionView.constant = footCell.iconsCollectionView.contentSize.height
self.view.layoutIfNeeded()
在 TableviewCell 中
var bars:[Bar] = []
didSet
self.iconsCollectionView.reloadData()
iconsCollectionView.setNeedsLayout()
self.layoutIfNeeded()
let items:CGFloat = CGFloat(bars.count + 1)
let value = (items / 3.0).rounded(.awayFromZero)
const_Height_CollectionView.constant = CGFloat((iconsCollectionView.collectionViewLayout as! UICollectionViewFlowLayout).itemSize.height * value)
self.layoutIfNeeded()
和
func cellTapped ()
iconsCollectionView.setNeedsLayout()
self.layoutIfNeeded()
self.setNeedsLayout()
let items:CGFloat = CGFloat(bars.count + 1)
let value = (items / 3.0).rounded(.awayFromZero)
const_Height_CollectionView.constant = CGFloat((iconsCollectionView.collectionViewLayout as! UICollectionViewFlowLayout).itemSize.height * value)
self.delegateCollection?.cellTapped(for: self)
【讨论】:
这对我不起作用。你能看出有什么问题吗?我已经更新了 git 存储库。同样在您的 gif 中,我可以看到某些行缺少加号按钮,它应该始终是最后一个 - 单击它后应该添加新项目,同时应该更新大小。 @Makalele 今天我离开了。因此,请更新您的 git 存储库,这就是我从那里得到的。我明天一定会去看的。 :☺ @Makalele 我已经解决了您的问题,请立即查看 :) 谢谢,这几乎可以工作了。但我发现 2 种情况下它不起作用:1) 在 iPad 模拟器上运行最初给出错误的高度:imgur.com/a/bYnJT 2) 屏幕旋转后高度错误(iPhone 8 模拟器):imgur.com/a/g5Txs @Makalele 这是因为集合视图布局。您可以看到,在行中的旋转项目增加后,它应该是 3。所以我已经对其进行了硬编码,并很快更新了答案。如果可行,请标记为已接受:)【参考方案2】:我认为你需要实现estimatedHeightForRowAt
,所以而不是下面的方法
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
return UITableViewAutomaticDimension
使用以下方法:
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat
return UITableViewAutomaticDimension
这应该可以,试试吧!
【讨论】:
抱歉,它似乎不起作用。您是否尝试过来自 github 的示例项目? 是的,我尝试在 iOS11.2 iPhone6 上加载项目并且工作正常。在第一次加载时,它的加载正确我的意思是我不需要滚动表格来查看图标。这就是你想要的对吗? @Makalele 我在 iPad Pro 上的 iOS 11.2 上运行,但工作不正常 :) TableView 应该将行高调整为正确的大小,当我向下滚动时,它还应该调整重用行的高度,但情况并非总是如此。【参考方案3】:UICollectionView
本身没有固有的内容大小,因此您必须为它创建一个显式的高度约束,正如您所说的那样。
如果布局似乎可以正常工作,但您在UIView-Encapsulated-Layout-Height
中遇到了无法满足的约束,请阅读以下内容。
这是UITableViewAutomaticDimension
工作方式的结果——它根据一些默认值设置单元格的高度(我仍然不知道它是如何获得该值的),然后使用自动布局来计算单元格想要的高度,然后更新UIView-Encapsulated-Layout-Height
以适应新的高度。但在此过程中,您的约束和 UIView-Encapsulated-Layout-Height
约束发生冲突。解决方案是将您的约束之一的优先级设置为collectionViewHeightConstraint.priority = UILayoutPriority(rawValue: 999)
。这样约束不会被破坏,并且自动布局将在没有警告的情况下工作 + 请注意,最终UIView-Encapsulated-Layout-Height
将更新到适当的高度,因此降低优先级不会阻止布局工作。请参阅my answer 以获取另一个类似问题以供参考。
编辑
我分叉了你的项目,修复了它,并创建了一个拉取请求(参见 github)。
我认为主要问题是一个非常微不足道的错误。您使用bars.count
来计算集合的内在大小,但最终您在集合中有bars.count + 1
项目(+ 图标)。因此,如果必须将 + 图标单独放在新行上,您的布局似乎无法正常工作。
所以改变
let rows = ceil(Double(bars.count) / Double(columnLayout.cellsPerRow))
到
let rows = ceil(Double(bars.count + 1) / Double(columnLayout.cellsPerRow))
在IconsCollectionView
的intrinsicContentSize
中。
还有其他一些东西我会改变,我在项目中改变了它 - 在故事板中的单元原型中。
首先,我删除了对集合高度的明确限制,设置为 88 点。你有内在尺寸,所以你不需要它(如果故事板抱怨,不要介意他们,他们不知道你实现了内在尺寸。
其次,底部的按钮没有被限制在单元格的底部。因此,单元格高度计算无法工作(您希望单元格调整自身大小以适应其内容,因此您需要将单元格的所有边都限制为其内容)。但这可能是其他人建议的结果,因为我认为较旧的提交可以正常工作。
第三,一个小注释,是关于您使用superviewWidth
计算大小的方式。我将其更改为selfView
,并更改了其值的计算方式。因为最终集合视图的宽度不等于单元格的宽度,而是单元格的宽度-16。这是因为collectionView 被限制为从单元格contentView
左侧开始8 个点,从单元格contentView
右侧开始8 个点。虽然这不是主要问题,但以后可能会引起一些问题。
最后,我在您异步加载项目时给您留下了评论。如果您异步加载它,则单元格很可能会在您取回数据之前呈现出来,并且您需要刷新 tableView 以适应新加载的数据。从概念上讲,这与动态扩展和折叠单元格相同 - 为此我将向您推荐 my answer to that question。
【讨论】:
我看到了这个答案,老实说试过了,但我的情况更复杂 - 只是无法让它工作。你能从 github 试试我的项目吗:)? @Makalele 我分叉了这个项目,修复了它(至少我相信它现在很好 - 我的测试不再显示问题),创建一个拉取请求(这样你就可以得到我的提交到你的存储库,请参阅 github),并更新了我的答案。希望这会对你有所帮助:) 不错的答案,但屏幕旋转后不起作用。我敢打赌“selfWidth”是原因,所以我用 (superview?.bounds.width - 16) 替换了它,但它仍然无法正常工作。我还尝试添加 viewWillTransition 来处理屏幕旋转更改,因此会重新计算项目大小,但这仍然不起作用。【参考方案4】:我下载了 git hub 源代码并解决了这个问题。您的代码非常适合设置单元格大小,问题在于您的列流布局。您设置为连续显示 4 个单元格,但根据您的源代码,只显示了 3 个单元格。我深入研究它,发现因此在图标集合中的“intrinsicContentSize”中计算错误的单元格数量看法。您在流布局函数中传递的超级视图的宽度导致问题。我想宽度有一些间距问题。所以这就是我的新代码的样子:
override var itemSize: CGSize
get
let itemWidth = ((superviewWidth - 50) / CGFloat(cellsPerRow)).rounded(.down)
return CGSize(width: itemWidth, height: itemWidth)
set
super.itemSize = newValue
override var intrinsicContentSize: CGSize
guard let columnLayout = columnLayout else return CGSize(width: 0, height: 0)
let itemSize = columnLayout.itemSize
let rows = ceil(Double(bars.count) / Double(columnLayout.cellsPerRow))
let w = columnLayout.superviewWidth
let h = itemSize.height * CGFloat(rows)
print("itemSize: \(itemSize.width), \(itemSize.height), intrinsicContentSize: \(w), \(h); rows = \(rows)")
return CGSize(width: w, height: h)
我只更改了这两个函数,您的代码运行良好。
如果你想要源代码,我也可以将它推送到 git 上。希望对您有所帮助!
【讨论】:
【参考方案5】:问题是:
// In DynamicCollectionView, when the icons fetch is not yet finished
override var intrinsicContentSize: CGSize
return contentSize.height // == 0
所以 tableView 不会计算正确的单元格高度。
解决方案是在您获取图标时给您的collectionView
一个高度。然后,当提取完成后,要求您的 tableView 重新加载相应的行。因此,您应该根据 collectionView
的布局而不是 contenSize
height 来计算高度:collectionView
的 contenSize
可能不正确,因为 reloadData
实际上是异步的.
【讨论】:
即使我从一开始就有数据,内容大小高度从一开始就是你所说的 0,但稍后它有一个大小(在 reloadData 完成后)。所以我必须在 reloadData 完成后重新加载行,这就是问题所在。但是重新加载会再次 reloadData :) 最好只修复 tableView 行的高度。 正如我所说:在您的 collectionLayout 中添加一个方法来计算您的 collectionView 的高度,而不是基于 contentSize 计算您的高度 我犯了一个错误:重新加载tableView行,而不是collectionView 我尝试自己计算intrinsicContentSize,但第一次调用时itemSize 是错误的。而且它可能会在屏幕旋转后发生变化,所以我不能只计算一次。查看我更新的 github 存储库。 您的 collectionView 宽度可能是错误的:它需要它的内在大小来获得它的宽度,但内在大小需要它的宽度。使用 tableviewrow 的宽度而不是 collectionview 的宽度来计算项目大小。以上是关于使用 UITableViewAutomaticDimension 制作带有嵌入式 UICollectionView 的 UITableView的主要内容,如果未能解决你的问题,请参考以下文章
带有嵌入 UICollectionView 的 UITableView 不起作用