使用 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))

IconsCollectionViewintrinsicContentSize 中。

还有其他一些东西我会改变,我在项目中改变了它 - 在故事板中的单元原型中。

首先,我删除了对集合高度的明确限制,设置为 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 来计算高度:collectionViewcontenSize 可能不正确,因为 reloadData 实际上是异步的.

【讨论】:

即使我从一开始就有数据,内容大小高度从一开始就是你所说的 0,但稍后它有一个大小(在 reloadData 完成后)。所以我必须在 reloadData 完成后重新加载行,这就是问题所在。但是重新加载会再次 reloadData :) 最好只修复 tableView 行的高度。 正如我所说:在您的 collectionLayout 中添加一个方法来计算您的 collectionView 的高度,而不是基于 contentSize 计算您的高度 我犯了一个错误:重新加载tableView行,而不是collectionView 我尝试自己计算intrinsicContentSize,但第一次调用时itemSize 是错误的。而且它可能会在屏幕旋转后发生变化,所以我不能只计算一次。查看我更新的 github 存储库。 您的 collectionView 宽度可能是错误的:它需要它的内在大小来获得它的宽度,但内在大小需要它的宽度。使用 tableviewrow 的宽度而不是 collectionview 的宽度来计算项目大小。

以上是关于使用 UITableViewAutomaticDimension 制作带有嵌入式 UICollectionView 的 UITableView的主要内容,如果未能解决你的问题,请参考以下文章

在 HeightOfRow 中使用自动调整大小

带有嵌入 UICollectionView 的 UITableView 不起作用

测试使用

第一篇 用于测试使用

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇