Создание UITableView со встроенным UICollectionView с использованием UITableViewAutomaticDimension

Я хочу создать UITableView с меткой заголовка и встроенным UICollectionView (который знает, что это размер) с некоторыми значками с помощью UITableViewAutomaticDimension. Проблема UITableView имеет проблему с определением высоты ячейки, когда у меня есть UICollectionView внутри. Я должен прокрутить UITableView для того, чтобы пересчитать размеры. Но даже тогда у него есть проблемы с высотой (это слишком большой, если он был повторно использован из большего). Поверх этого значков внутри UICollectionView не известны из begginging, но они предназначены для загрузки от сервера.

Я также попытался создать ограничение высоты для UICollectionView, но таким образом я получаю "неспособность одновременно удовлетворять ограничения", вызванные конфликтом с UIView-Encapsulated-Layout-Height и мое собственное ограничение все равно удаляется.

Я создал репозиторий GitHub с образцом проекта (я сделал это как можно проще):

https://github.com/piotrros/CollectionViewInTableView

5 ответов


как вы делаете очень много вещей в cellForRow поэтому ему нужно время, чтобы подготовиться, и поэтому при прокрутке он не отображается должным образом

проверьте следующие вещи.

ViewController.Свифт!--21-->

ввиду загрузки

добавить 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.Свифт!--21-->

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 форму вашего раскадровка и просто дайте ведущие, трейлинг, верхние и нижние ограничения (ничего сложного )

enter image description here

здесь выводится

enter image description here

надеюсь, что это полезно для вас

ИЗМЕНИТЬ/ОБНОВИТЬ

у вас есть много проблем в вашем демо-проекте. Я внес много изменений в ваш демо-проект.

копировать и вставлять XML in раскадровка.

НЕ ЗАБУДЬТЕ ПОДКЛЮЧИТЬ ОГРАНИЧЕНИЕ ВЫСОТЫ

вот полная раскадровка 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" width="375" height="667"/>
                        <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" width="375" height="667"/>
                                <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" width="375" height="196"/>
                                        <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" width="375" height="195.5"/>
                                            <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" width="359" height="20.5"/>
                                                    <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" width="359" height="20.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" width="359" height="92.5"/>
                                                    <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" width="50" height="50"/>
                                                        <size key="headerReferenceSize" width="0.0" height="0.0"/>
                                                        <size key="footerReferenceSize" width="0.0" height="0.0"/>
                                                        <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" width="50" height="50"/>
                                                            <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" width="50" height="50"/>
                                                                <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" width="50" height="50"/>
                                                                    </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" width="359" height="30"/>
                                                    <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.Свифт!--21-->

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.Свифт!--21-->

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.Свифт!--21-->

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()
        }
    }

}

выход

enter image description here

изменить / Обновление 2

я не знал о поддержке ориентации и поддержке ipad.

теперь, когда ориентация изменяется, мы должны перестроить представление коллекции.

Итак, логика

всего элементов + 1 (+1 из-за этого плюс значок )

размер элемента * (всего элементов / 3).округлые

предположим, у вас есть 7 пунктов

так размер деталя 93 (в строку) * (8 / 3).округлый = 279

так вот вам нужно управлять некоторыми жестко закодированными значениями в соответствии с вашими требованиями для iPad и ландшафтного режима. На данный момент я рассматриваю 3 объекта в строке так же, как дизайн iPhone.

здесь Hardcoded номер ячейки 3 Вы можете управлять своими собственными.

Шаг 1:

добавить следующий метод в viewContorller.Свифт!--16-->

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()

        }
    }

In 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)

    }

Я думаю, вам нужно реализовать estimatedHeightForRowAt, поэтому вместо метода ниже

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableViewAutomaticDimension
}

использовать ниже метод:

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableViewAutomaticDimension
}

Это должно работать, попробуйте!


UICollectionView сам по себе не имеет внутреннего размера контента, поэтому вам нужно создать явное ограничение высоты для него, как вы говорите.

если макет, кажется, работает, но вы получаете неудовлетворительные ограничения с UIView-Encapsulated-Layout-Height, тогда прочитайте следующие.

это результат того, как UITableViewAutomaticDimension works-он устанавливает высоту ячейки на основе некоторого значения по умолчанию (я все еще не знаю, как он получает это значение), затем использует autolayout для вычисления высота вашей ячейки хочет, а затем обновляет UIView-Encapsulated-Layout-Height чтобы соответствовать новой высоты. Но в процессе ваши ограничения и UIView-Encapsulated-Layout-Height ограничение вступает в конфликт. Решение состоит в том, чтобы установить приоритет одного из ваших ограничений на collectionViewHeightConstraint.priority = UILayoutPriority(rawValue: 999). Таким образом, ограничения не будут нарушены, и autolayout будет работать без предупреждений + обратите внимание, что в конце UIView-Encapsulated-Layout-Height будет обновляться до нужной высоты, поэтому снижение приоритета не помешает макету работать. См.мой ответ: к другому подобному вопрос Для справки.

редактировать

я разветвил ваш проект, исправил его и создал запрос на вытягивание (см. github).

основная проблема, я считаю, была очень тривиальной ошибкой. Вы использовали bars.count чтобы рассчитать внутренний размер коллекции, но в конце концов у вас было bars.count + 1 элементов в коллекции (значок+). Поэтому, если значок + должен был быть помещен в новую строку, казалось, что ваш макет не работает.

так просто изменить

let rows = ceil(Double(bars.count) / Double(columnLayout.cellsPerRow))

to

let rows = ceil(Double(bars.count + 1) / Double(columnLayout.cellsPerRow))

на intrinsicContentSize на IconsCollectionView.

были другие вещи, которые я бы изменил, и я изменил его в проекте - в прототипе вашей ячейки в раскадровках.

во-первых, я удалил это явное ограничение на высоту набора коллекции, равную 88 точкам. У вас есть внутренний размер, поэтому вам это не нужно (если раскадровки жалуются, не обращайте на них внимания, они не знают, что вы реализовали внутренний размер.

во-вторых, кнопка внизу не была ограничена нижней частью ячейки. Таким образом, расчет высоты ячейки не может работать (вы хотите, чтобы размер ячейки соответствовал ее содержимому, поэтому вам нужно ограничить все стороны ячейки ее содержимым). Но это, возможно, было результатом чьих-то советов, потому что я думаю, что старые коммиты работали.

в-третьих, незначительная нота, к тому, как вы использовали superviewWidth для расчета размера. Я изменил его на selfView, и изменил способ вычисления его значения. Потому что в конце ширина представления коллекции не равна ширине ячейки, а ширине ячейки -16. Это потому, что collectionView был ограничен, чтобы начать 8 точек слева от ячейки contentView, и 8 точек справа от ячейки contentView. Хотя это не главная проблема, она может вызвать некоторые проблемы позже.

наконец, я оставил вам комментарий в асинхронной загрузке элементов. Если вы загрузите его асинхронно ячейка, скорее всего, будет представлена до того, как вы получите свои данные, и вы захотите обновить tableView для адаптации к недавно загруженным данным. Концептуально это то же самое, что динамически расширяющиеся и коллапсирующие ячейки - для этого я отсылаю вас к мой ответ на этот вопрос.


Я загрузил исходный код Git hub и исправил проблему. Ваш код идеально подходит для установки размера ячейки , проблема заключается в компоновке потока столбцов. Вы устанавливали отображение 4 ячеек подряд, но в соответствии с вашим исходным кодом отображалось только 3 ячейки.Я копнул глубже и обнаружил, что из-за этого количество ячеек вычисляется неправильно в вашем "intrinsicContentSize" в представлении коллекции значков. Ширина super view, которую вы передаете в функции компоновки потока, вызывает проблема. я думаю, была какая-то проблема с расстоянием для ширины. Итак, вот как выглядит мой новый код:

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)
    }

Я изменил только эти две функции и ваш код прекрасно работал. enter image description here

Если вы хотите исходный код, я могу нажать его на git тоже. Надеюсь, это поможет вам!


проблема :

// In DynamicCollectionView, when the icons fetch is not yet finished
override var intrinsicContentSize: CGSize {
    return contentSize.height // == 0
}

таким образом, tableView не вычисляет правильные высоты ячеек.

решение дать по вашему collectionView высота, когда вы получаете значки. Затем, когда fetch будет сделано, спросите ваш tableView для перезагрузки соответствующей строки. Поэтому вы должны основывать свой расчет высоты на макете вашего collectionView а не contenSize высота: the contenSize на collectionView может быть неправильным потому что reloadData на самом деле асинхронные.