I want to create an UITableView
with Title label and embedded UICollectionView
(which knows it's size) with some icons using UITableViewAutomaticDimension
. The problem is UITableView
has a problem with figuring out cell height when I have UICollectionView
inside. I have to scroll an UITableView
in order for it to recalculate sizes. But even then it has problems with height (it's too big if it was reused from bigger one). On top of that icons inside UICollectionView
aren't known from the begginging, but they are intended to be loaded from the server.
I've also tried to create a height constraint for an UICollectionView
, but this way I'm getting "Unable to simultaneously satisfy constraints" caused by conflict with UIView-Encapsulated-Layout-Height
and my own constraint is being removed anyway.
I've created a GitHub repository with a sample project (I did it as simple as possible):
5 Answers
Answers 1
As you are doing very much stuff in cellForRow
so it need time to ready and so when you scroll it is not showing properly
Check following things.
ViewController.swift
In View Did load
Add tableView.rowHeight = UITableViewAutomaticDimension
and replace this method
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() } }
And I have removed StackView Form your storyboard and just give leading , trailing ,top and bottom constraints (Nothing complicated )
Here is output
Hope it is helpful to you
EDIT/UPDATE
YOu have many issues in your demo project. I have made many changes in your demo project.
Copy and paste XML
in storyboard.
DON'T FORGOT TO CONNECT HEIGHT CONSTRAINT
Here is complete storyboard 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() } } }
OUTPUT
EDIT / UPDATE 2
I was not aware about orientation supports and ipad support.
now when the orientation changes we have to re-layout the collection view.
So logic is
total items + 1 (+ 1 because of that plus icon )
Item Size * (total Items / 3).rounded
suppose you have 7 Items
so item size is 93 (Per row) * ( 8 / 3).rounded = 279
So here you need to manage some hardcoded values as per your requirement for iPad and Landscape mode. For now I am considering 3 objects per row same as iPhone design.
Here Hardcoded cell number is 3 You can manage your own.
Step1:
Add Following method in 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() }
Replace 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 }
And
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() } }
and
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) }
Answers 2
I think you need to implement estimatedHeightForRowAt
, so instead of below method
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return UITableViewAutomaticDimension }
Use below method:
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { return UITableViewAutomaticDimension }
This should work, try it!
Answers 3
The problem is :
// In DynamicCollectionView, when the icons fetch is not yet finished override var intrinsicContentSize: CGSize { return contentSize.height // == 0 }
So the tableView does not calculate the right cell heights.
The solution is to give at your collectionView
a height while you are fetching the icons. Then, when the fetch is done, ask your tableView to reload the corresponding row. Therefore, you should base your height calculation on the layout of your collectionView rather than on contenSize height : the contenSize of the collectionView may be incorrect because reloadData
is actually asynchronous.
Answers 4
I downloaded the git hub source code an fixed the issue. Your code is perfect for setting the cell size , the problem lies you column flow layout. You were setting to show 4 cells in a row, but according to your source code only 3 cells were displayed.I digged deeper into it and found out that because of this the number of cells are calculated wrong in your "intrinsicContentSize" in Icons collection view. The width of super view you are passing in the flow layout function is causing problem. i guess there was some spacing issue for the width. So here is how my new code looks :
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) }
I have changed only these two function and your code worked perfectly.
If you want source code I can push it on git too. Hope this helps you out!
Answers 5
UICollectionView
itself does not have an intrinsic content size, thus you have to create an explicit height constraint for it, as you say you did.
If the layout seems to be working then, but you are getting unsatisfiable constraints with UIView-Encapsulated-Layout-Height
, then read the following.
That's a result of how UITableViewAutomaticDimension
works - it sets height of the cell based on some default value (I still don't know how does it get that value), then uses autolayout to calculate the height your cell wants, and then updates the UIView-Encapsulated-Layout-Height
to fit the new height. But in the process your constraints and the UIView-Encapsulated-Layout-Height
constraint gets into conflict. The solution is to set the priority of one of your constraints to collectionViewHeightConstraint.priority = UILayoutPriority(rawValue: 999)
. This way the constraints won't get broken, and autolayout will work without warnings + note that in the end UIView-Encapsulated-Layout-Height
will get updated to the proper height, so lowering priority will not prevent layout to work. See my answer to another similar question for reference.
EDIT
I forked your project, fixed it, and created a pull request (see github).
The main problem I believe was a very trivial bug. You used bars.count
to calculate the intrinsic size of the collection, but in the end you had bars.count + 1
items in collection (the + icon). Therefore if + icon had to be put on a new row alone, it seemed that your layout wasn't working.
So just change
let rows = ceil(Double(bars.count) / Double(columnLayout.cellsPerRow))
to
let rows = ceil(Double(bars.count + 1) / Double(columnLayout.cellsPerRow))
in intrinsicContentSize
of the IconsCollectionView
.
There were other things I would change, and I changed it in the project - in your cell prototype in storyboards.
First, I removed that explicit constraint on height of the collection set to 88 points. You have intrinsic size, so you don't need that (if storyboards complain, don't mind them, they do not know that you implemented intrinsic size.
Second, the button at the bottom was not constrained to the bottom of the cell. Thus the cell height calculation could not work (you want the cell to size itself to fit its contents, so you need to constrain all the sides of the cell to its contents). But that maybe was a result of someone else's advices, because I think older commits had it working.
Third, a minor note, is to the way you used superviewWidth
to calculate the size. I changed it to selfView
, and changed the way its value was calculated. Because in the end the width of collection view is not equal to cell's width, but to cell's width -16. That's because collectionView was constrained to start 8 points from the left of the cell's contentView
, and 8 points from the right of the cell's contentView
. Although that was not the main issue, it might cause some problems later.
Finally, I left you a comment in you async loading of the items. If you load it asynchronously, the cell will most probably get presented before you get your data back, and you will want to refresh the tableView to adapt to newly loaded data. Conceptually, that's the same as dynamically expanding and collapsing cells - for that I will refer you to my answer to that question. Please, if you find it helpful, leave an upvote there too to make it more visible.
0 comments:
Post a Comment