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

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

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

Я создал GitHub-репозиторий с примером проекта (я сделал это максимально просто):

https://github.com/piotrros/CollectionViewInTableView

5 ответов

Решение

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

Вот выходной

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

EDIT/UPDATE

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

Скопировать и вставить 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" 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.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)

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

так что размер предмета составляет 93 (на строку) * ( 8 / 3). Ground = 279

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

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

Шаг 1:

Добавить следующий метод в viewContorller.swift

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)

    }

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

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

Это результат того, как UITableViewAutomaticDimension работает - он устанавливает высоту ячейки на основе некоторого значения по умолчанию (я до сих пор не знаю, как он получает это значение), затем использует 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))

в

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

в intrinsicContentSize из IconsCollectionView,

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

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

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

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

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

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

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

Используйте метод ниже:

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

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

Проблема в:

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

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

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

Я скачал исходный код 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. Надеюсь, что это помогает вам!

Другие вопросы по тегам