Создание UITableView со встроенным UICollectionView с использованием UITableViewAutomaticDimension
Я хочу создать UITableView
с заголовком и встроенным UICollectionView
(который знает его размер) с использованием некоторых значков UITableViewAutomaticDimension
, Проблема в UITableView
есть проблема с вычислением высоты клетки, когда у меня есть UICollectionView
внутри. Я должен прокрутить UITableView
чтобы он пересчитал размеры. Но даже тогда у него есть проблемы с высотой (он слишком велик, если его использовать повторно с большего размера). Поверх этих значков внутри UICollectionView
не известны с самого начала, но они предназначены для загрузки с сервера.
Я также пытался создать ограничение высоты для UICollectionView
, но таким образом я получаю "Невозможно одновременно удовлетворить ограничения", вызванные конфликтом с UIView-Encapsulated-Layout-Height
и мое собственное ограничение все равно снимается.
Я создал GitHub-репозиторий с примером проекта (я сделал это максимально просто):
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. Надеюсь, что это помогает вам!