UICollectionView - dynamic cell height?

I need to display a bunch of collectionViewCells with different heights. the views are too complex and I don't want to manually calculate the expected height. I want to provide automatic layout for calculating cell height

Calling dequeueReusableCellWithReuseIdentifier outside cellForItemAtIndexPath aborts collectionView and causes it to fail

Another problem is that the cell is not in a separate xib, so I cannot manually create a temporary instance and use it to calculate the height.

Any solutions for this?

 public func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { var cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellIdentifier, forIndexPath: indexPath) as UICollectionViewCell configureCell(cell, item: items[indexPath.row]) cell.contentView.setNeedsLayout() cell.contentView.layoutIfNeeded() return cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize) } 

EDIT:

A crash occurs as soon as dequeueReusableCellWithReuseIdentifier is called. If I don’t name this method and instead return its size, everything will work fine, and the cells will be displayed without the estimated size

negative or zero dimensions are not supported in the flow layout

 2015-01-26 18:24:34.231 [13383:9752256] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]' *** First throw call stack: ( 0 CoreFoundation 0x00000001095aef35 __exceptionPreprocess + 165 1 libobjc.A.dylib 0x0000000109243bb7 objc_exception_throw + 45 2 CoreFoundation 0x0000000109499f33 -[__NSArrayM objectAtIndex:] + 227 3 UIKit 0x0000000107419d9c -[UICollectionViewFlowLayout _getSizingInfos] + 842 4 UIKit 0x000000010741aca9 -[UICollectionViewFlowLayout _fetchItemsInfoForRect:] + 526 5 UIKit 0x000000010741651f -[UICollectionViewFlowLayout prepareLayout] + 257 6 UIKit 0x000000010742da10 -[UICollectionViewData _prepareToLoadData] + 67 7 UIKit 0x00000001074301c6 -[UICollectionViewData layoutAttributesForItemAtIndexPath:] + 44 8 UIKit 0x00000001073fddb1 -[UICollectionView _dequeueReusableViewOfKind:withIdentifier:forIndexPath:viewCategory:] + 248 9 0x00000001042b824c _TFC1228BasePaginatingViewController14collectionViewfS0_FTCSo16UICollectionView6layoutCSo22UICollectionViewLayout22sizeForItemAtIndexPathCSo11NSIndexPath_VSC6CGSize + 700 10 0x00000001042b83d4 _TToFC1228BasePaginatingViewController14collectionViewfS0_FTCSo16UICollectionView6layoutCSo22UICollectionViewLayout22sizeForItemAtIndexPathCSo11NSIndexPath_VSC6CGSize + 100 11 UIKit 0x0000000107419e2e -[UICollectionViewFlowLayout _getSizingInfos] + 988 12 UIKit 0x000000010741aca9 -[UICollectionViewFlowLayout _fetchItemsInfoForRect:] + 526 13 UIKit 0x000000010741651f -[UICollectionViewFlowLayout prepareLayout] + 257 14 UIKit 0x000000010742da10 -[UICollectionViewData _prepareToLoadData] + 67 15 UIKit 0x000000010742e0e9 -[UICollectionViewData validateLayoutInRect:] + 54 16 UIKit 0x00000001073f67b8 -[UICollectionView layoutSubviews] + 170 17 UIKit 0x0000000106e3c973 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 521 18 QuartzCore 0x0000000106b0fde8 -[CALayer layoutSublayers] + 150 19 QuartzCore 0x0000000106b04a0e _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380 20 QuartzCore 0x0000000106b0487e _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24 21 QuartzCore 0x0000000106a7263e _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 242 22 QuartzCore 0x0000000106a7374a _ZN2CA11Transaction6commitEv + 390 23 QuartzCore 0x0000000106a73db5 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 89 24 CoreFoundation 0x00000001094e3dc7 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23 25 CoreFoundation 0x00000001094e3d20 __CFRunLoopDoObservers + 368 26 CoreFoundation 0x00000001094d9b53 __CFRunLoopRun + 1123 27 CoreFoundation 0x00000001094d9486 CFRunLoopRunSpecific + 470 28 GraphicsServices 0x000000010be869f0 GSEventRunModal + 161 29 UIKit 0x0000000106dc3420 UIApplicationMain + 1282 30 0x000000010435c709 main + 169 31 libdyld.dylib 0x000000010a0f2145 start + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException 
+48
ios swift uicollectionview
Jan 27 '15 at 1:38
source share
10 answers

Here is a Ray Wenderlich tutorial that shows how to use AutoLayout for the dynamic size of UITableViewCell s. I would think that would be the same for a UICollectionViewCell .

Basically, you finish deleting and setting up the cell prototype and capturing its height. After reading this article, I decided NOT to implement this method and just write a clear explicit size code.

Here is what I consider the β€œsecret sauce” for the whole article:

 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return [self heightForBasicCellAtIndexPath:indexPath]; } - (CGFloat)heightForBasicCellAtIndexPath:(NSIndexPath *)indexPath { static RWBasicCell *sizingCell = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sizingCell = [self.tableView dequeueReusableCellWithIdentifier:RWBasicCellIdentifier]; }); [self configureBasicCell:sizingCell atIndexPath:indexPath]; return [self calculateHeightForConfiguredSizingCell:sizingCell]; } - (CGFloat)calculateHeightForConfiguredSizingCell:(UITableViewCell *)sizingCell { [sizingCell setNeedsLayout]; [sizingCell layoutIfNeeded]; CGSize size = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; return size.height + 1.0f; // Add 1.0f for the cell separator height } 




EDIT: I did some research on your accident and decided that there is no way to do this without a special XIB. Although this is a little disappointing, you should be able to cut and paste from your storyboard into a custom empty XIB.

Once you do this, you will get the following code:

 // ViewController.m #import "ViewController.h" #import "CollectionViewCell.h" @interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout> { } @property (weak, nonatomic) IBOutlet CollectionViewCell *cell; @property (weak, nonatomic) IBOutlet UICollectionView *collectionView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor lightGrayColor]; [self.collectionView registerNib:[UINib nibWithNibName:@"CollectionViewCell" bundle:nil] forCellWithReuseIdentifier:@"cell"]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; NSLog(@"viewDidAppear..."); } - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { return 1; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return 50; } - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section { return 10.0f; } - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section { return 10.0f; } - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { return [self sizingForRowAtIndexPath:indexPath]; } - (CGSize)sizingForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *title = @"This is a long title that will cause some wrapping to occur. This is a long title that will cause some wrapping to occur."; static NSString *subtitle = @"This is a long subtitle that will cause some wrapping to occur. This is a long subtitle that will cause some wrapping to occur."; static NSString *buttonTitle = @"This is a really long button title that will cause some wrapping to occur."; static CollectionViewCell *sizingCell = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sizingCell = [[NSBundle mainBundle] loadNibNamed:@"CollectionViewCell" owner:self options:nil][0]; }); [sizingCell configureWithTitle:title subtitle:[NSString stringWithFormat:@"%@: Number %d.", subtitle, (int)indexPath.row] buttonTitle:buttonTitle]; [sizingCell setNeedsLayout]; [sizingCell layoutIfNeeded]; CGSize cellSize = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; NSLog(@"cellSize: %@", NSStringFromCGSize(cellSize)); return cellSize; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { static NSString *title = @"This is a long title that will cause some wrapping to occur. This is a long title that will cause some wrapping to occur."; static NSString *subtitle = @"This is a long subtitle that will cause some wrapping to occur. This is a long subtitle that will cause some wrapping to occur."; static NSString *buttonTitle = @"This is a really long button title that will cause some wrapping to occur."; CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath]; [cell configureWithTitle:title subtitle:[NSString stringWithFormat:@"%@: Number %d.", subtitle, (int)indexPath.row] buttonTitle:buttonTitle]; return cell; } @end 

The code above (along with the very basic subclass of UICollectionViewCell and the associated XIB) gives me the following:

enter image description here

+23
Jan 27 '15 at 1:47
source share

I just ran into this problem in UICollectionView and how I solved it, similar to the above, but in a clean way, UICollectionView.

  • Create a custom UICollectionViewCell that contains everything you will fill to make it dynamic. I created my own .xib for myself, as it looks like the easiest approach.

  • Add constraints to this .xib that allow you to compute a cell from top to bottom. Recalibration will not work if you have not specified the entire height. Say you have a top view, then an inscription below it and another inscription. You will need to connect the restrictions to the top of the cell at the top of this view, then to the bottom of the window at the top of the first label, the bottom of the first label to the top of the second label and the bottom of the second label to the bottom of the cell.

  • Load .xib into the viewcontroller and register it using the viewDidLoad collection on viewDidLoad

     let nib = UINib(nibName: CustomCellName, bundle: nil) self.collectionView!.registerNib(nib, forCellWithReuseIdentifier: "customCellID")` 
  • Load the second copy of this xib into the class and save it as a property so that you can use it to determine the size of what should be in this cell

     let sizingNibNew = NSBundle.mainBundle().loadNibNamed(CustomCellName, owner: CustomCellName.self, options: nil) as NSArray self.sizingNibNew = (sizingNibNew.objectAtIndex(0) as? CustomViewCell)! 
  • UICollectionViewFlowLayoutDelegate in your controller. The method that matters is called sizeForItemAtIndexPath . Inside this method, you will need to pull the data from the data source that is associated with this cell from indexPath. Then configure sizingCell and call preferredLayoutSizeFittingSize . The method returns CGSize, which will consist of the width minus the insertion of the content and the height returned from self.sizingCell.preferredLayoutSizeFittingSize(targetSize) .

     override func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { guard let data = datasourceArray?[indexPath.item] else { return CGSizeZero } let sectionInset = self.collectionView?.collectionViewLayout.sectionInset let widthToSubtract = sectionInset!.left + sectionInset!.right let requiredWidth = collectionView.bounds.size.width let targetSize = CGSize(width: requiredWidth, height: 0) sizingNibNew.configureCell(data as! CustomCellData, delegate: self) let adequateSize = self.sizingNibNew.preferredLayoutSizeFittingSize(targetSize) return CGSize(width: (self.collectionView?.bounds.width)! - widthToSubtract, height: adequateSize.height) } 
  • In the native cell class, you need to override awakeFromNib and tell the contentView that its size should be flexible

      override func awakeFromNib() { super.awakeFromNib() self.contentView.autoresizingMask = [UIViewAutoresizing.FlexibleHeight] } 
  • In custom cell override layoutSubviews

      override func layoutSubviews() { self.layoutIfNeeded() } 
  • In the custom cell set class, preferredLayoutSizeFittingSize . Here you will need any tricks on the items that are laid out. If its shortcut you will need to specify what its preferred MaxWidth should be.

     preferredLayoutSizeFittingSize(targetSize: CGSize)-> CGSize { let originalFrame = self.frame let originalPreferredMaxLayoutWidth = self.label.preferredMaxLayoutWidth var frame = self.frame frame.size = targetSize self.frame = frame self.setNeedsLayout() self.layoutIfNeeded() self.label.preferredMaxLayoutWidth = self.questionLabel.bounds.size.width // calling this tells the cell to figure out a size for it based on the current items set let computedSize = self.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize) let newSize = CGSize(width:targetSize.width, height:computedSize.height) self.frame = originalFrame self.questionLabel.preferredMaxLayoutWidth = originalPreferredMaxLayoutWidth return newSize } 

All these steps should give you the right sizes. If you get 0 or other funky numbers than you incorrectly configured your limits.

+24
Dec 05 '15 at 7:21
source share

We can maintain dynamic height for a collection view cell without xib (only using storyboard).

 - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { NSAttributedString* labelString = [[NSAttributedString alloc] initWithString:@"Your long string goes here" attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17.0]}]; CGRect cellRect = [labelString boundingRectWithSize:CGSizeMake(cellWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:nil]; return CGSizeMake(cellWidth, cellRect.size.height); } 

Make sure numberOfLines in IB must be 0.

+13
Jan 13 '17 at 7:52
source share

TL; DR: Scan to the image, then check the working draft here.

Updating my answer for a simpler solution that I found ..

In my case, I wanted to fix the width and have cells of variable height. I wanted to use a reusable solution that handled rotation and did not require much intervention.

I came to systemLayoutFitting(...) that redefined (just) systemLayoutFitting(...) in the collection cell (in this case, it is the base class for me), and first defeated the UICollectionView attempt to set the wrong dimension in the contentView , adding a limit to the well-known Dimension, in this case the width.

 class EstimatedWidthCell: UICollectionViewCell { override init(frame: CGRect) { super.init(frame: frame) contentView.translatesAutoresizingMaskIntoConstraints = false } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) contentView.translatesAutoresizingMaskIntoConstraints = false } override func systemLayoutSizeFitting( _ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize { width.constant = targetSize.width 

and then returns the final size for the cell β€” used to (and this seems like an error) measure the cell itself, but not the contentView β€” which is otherwise limited by the conflicting size (hence the restriction above). To calculate the correct cell size, I use a lower priority for the dimension that I wanted to float, and get back the height needed to fit the content within the width that I want to fix:

  let size = contentView.systemLayoutSizeFitting( CGSize(width: targetSize.width, height: 1), withHorizontalFittingPriority: .required, verticalFittingPriority: verticalFittingPriority) print("\(#function) \(#line) \(targetSize) -> \(size)") return size } lazy var width: NSLayoutConstraint = { return contentView.widthAnchor .constraint(equalToConstant: bounds.size.width) .isActive(true) }() } 

But where does this width come from? It is configured with an estimatedItemSize element size in the layout view stream of the collection:

 lazy var collectionView: UICollectionView = { let view = UICollectionView(frame: CGRect(), collectionViewLayout: layout) view.backgroundColor = .cyan view.translatesAutoresizingMaskIntoConstraints = false return view }() lazy var layout: UICollectionViewFlowLayout = { let layout = UICollectionViewFlowLayout() let width = view.bounds.size.width // should adjust for inset layout.estimatedItemSize = CGSize(width: width, height: 10) layout.scrollDirection = .vertical return layout }() 

Finally, to handle the rotation, I implement trailCollectionDidChange to invalidate the layout:

 override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { layout.estimatedItemSize = CGSize(width: view.bounds.size.width, height: 10) layout.invalidateLayout() super.traitCollectionDidChange(previousTraitCollection) } 

The end result looks like this:

enter image description here

And I posted a working sample here.

+9
Nov 22 '17 at 0:23
source share

Swift 4. *

I created Xib for a UICollectionViewCell, which seems like a good approach.

 extension ViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return size(indexPath: indexPath) } private func size(for indexPath: IndexPath) -> CGSize { // load cell from Xib let cell = Bundle.main.loadNibNamed("ACollectionViewCell", owner: self, options: nil)?.first as! ACollectionViewCell // configure cell with data in it let data = self.data[indexPath.item] cell.configure(withData: data) cell.setNeedsLayout() cell.layoutIfNeeded() // width that you want let width = collectionView.frame.width let height: CGFloat = 0 let targetSize = CGSize(width: width, height: height) // get size with width that you want and automatic height let size = cell.contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: .defaultHigh, verticalFittingPriority: .fittingSizeLevel) // if you want height and width both to be dynamic use below // let size = cell.contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize) return size } } 

# note: I do not recommend setting the image when setting up the data in this case, which determines the size. This gave me a distorted / unwanted result. Customizing the texts only gave me the result below.

enter image description here

+4
Aug 21 '18 at 11:25
source share

This seems to be a pretty popular question, so I will try to make my modest contribution.




The code below is a Swift 4 solution for installation without a storyboard. It uses some approaches from the previous answers, so it prevents the Auto Layout warning that occurs when the device is rotated.

Sorry if the code examples are a bit long. I want to provide an β€œeasy to use” solution, hosted entirely on StackOverflow. If you have any suggestions for a post, share the idea, and I will update it accordingly.

Setup:

Two classes: ViewController.swift and MultilineLabelCell.swift - A cell containing one UILabel .

MultilineLabelCell.swift

 import UIKit class MultilineLabelCell: UICollectionViewCell { static let reuseId = "MultilineLabelCellReuseId" private let label: UILabel = UILabel(frame: .zero) override init(frame: CGRect) { super.init(frame: frame) layer.borderColor = UIColor.red.cgColor layer.borderWidth = 1.0 label.numberOfLines = 0 label.lineBreakMode = .byWordWrapping let labelInset = UIEdgeInsets(top: 10, left: 10, bottom: -10, right: -10) contentView.addSubview(label) label.translatesAutoresizingMaskIntoConstraints = false label.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor, constant: labelInset.top).isActive = true label.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor, constant: labelInset.left).isActive = true label.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor, constant: labelInset.right).isActive = true label.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor, constant: labelInset.bottom).isActive = true label.layer.borderColor = UIColor.black.cgColor label.layer.borderWidth = 1.0 } required init?(coder aDecoder: NSCoder) { fatalError("Storyboards are quicker, easier, more seductive. Not stronger then Code.") } func configure(text: String?) { label.text = text } override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes { label.preferredMaxLayoutWidth = layoutAttributes.size.width - contentView.layoutMargins.left - contentView.layoutMargins.left layoutAttributes.bounds.size.height = systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height return layoutAttributes } } 

ViewController.swift

 import UIKit let samuelQuotes = [ "Samuel says", "Add different length strings here for better testing" ] class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { private(set) var collectionView: UICollectionView // Initializers init() { // Create new 'UICollectionView' and set 'UICollectionViewFlowLayout' as its layout collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) super.init(nibName: nil, bundle: nil) } required init?(coder aDecoder: NSCoder) { // Create new 'UICollectionView' and set 'UICollectionViewFlowLayout' as its layout collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) super.init(coder: aDecoder) } override func viewDidLoad() { super.viewDidLoad() title = "Dynamic size sample" // Register Cells collectionView.register(MultilineLabelCell.self, forCellWithReuseIdentifier: MultilineLabelCell.reuseId) // Add 'coolectionView' to display hierarchy and setup its appearance view.addSubview(collectionView) collectionView.backgroundColor = .white collectionView.contentInsetAdjustmentBehavior = .always collectionView.contentInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) // Setup Autolayout constraints collectionView.translatesAutoresizingMaskIntoConstraints = false collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true collectionView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true collectionView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true // Setup 'dataSource' and 'delegate' collectionView.dataSource = self collectionView.delegate = self (collectionView.collectionViewLayout as! UICollectionViewFlowLayout).estimatedItemSize = UICollectionViewFlowLayout.automaticSize (collectionView.collectionViewLayout as! UICollectionViewFlowLayout).sectionInsetReference = .fromLayoutMargins } // MARK: - UICollectionViewDataSource - func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MultilineLabelCell.reuseId, for: indexPath) as! MultilineLabelCell cell.configure(text: samuelQuotes[indexPath.row]) return cell } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return samuelQuotes.count } // MARK: - UICollectionViewDelegateFlowLayout - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let sectionInset = (collectionViewLayout as! UICollectionViewFlowLayout).sectionInset let referenceHeight: CGFloat = 100 // Approximate height of your cell let referenceWidth = collectionView.safeAreaLayoutGuide.layoutFrame.width - sectionInset.left - sectionInset.right - collectionView.contentInset.left - collectionView.contentInset.right return CGSize(width: referenceWidth, height: referenceHeight) } } 

To run this example, create a new AppDelegate project, create the appropriate files, and replace the contents of the AppDelegate following code:

 import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? var navigationController: UINavigationController? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) if let window = window { let vc = ViewController() navigationController = UINavigationController(rootViewController: vc) window.rootViewController = navigationController window.makeKeyAndVisible() } return true } } 
+3
Nov 01 '18 at 13:13
source share

I followed the steps mentioned in this SO and everything is fine, except when my Collection View has less data (text) to make it wide enough. Checking the documentation in systemLyaoutSizeFittingSize , I have this solution, so my cell accepts the width as I requested:

 - (CGSize)calculateSizeForSizingCell:(UICollectionViewCell *)sizingCell width:(CGFloat)width { CGRect frame = sizingCell.frame; frame.size.width = width; sizingCell.frame = frame; [sizingCell setNeedsLayout]; [sizingCell layoutIfNeeded]; CGSize size = [sizingCell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel]; return size; } 

Hope this helps someone.

 - (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize NS_AVAILABLE_IOS(6_0); 

Apple doc:

-LayoutSizeFittingSize: withHorizontalFittingPriority: verticalFittingPriority: UILayoutPriorityFittingSizeLevel .

" " Apple:

- [UIView systemLayoutSizeFittingSize:] , (). UILayoutPriorityFittingSizeLevel - , . . , . .

, ( ) UILayoutPriorityRequired .

+1
10 . '17 15:52
source share

bolnad answer 4.

, :

 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { // Configure your cell sizingNibNew.configureCell(data as! CustomCellData, delegate: self) // We use the full width minus insets let width = collectionView.frame.size.width - collectionView.sectionInset.left - collectionView.sectionInset.right // Constrain our cell to this width let height = sizingNibNew.systemLayoutSizeFitting(CGSize(width: width, height: .infinity), withHorizontalFittingPriority: UILayoutPriorityRequired, verticalFittingPriority: UILayoutPriorityFittingSizeLevel).height return CGSize(width: width, height: height) } 
+1
21 . '17 16:38
source share

, , .

* : Nib, contentView

 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let cell = YourCollectionViewCell.instantiateFromNib() cell.frame.size.width = collectionView.frame.width cell.data = viewModel.data[indexPath.item] let resizing = cell.systemLayoutSizeFitting(UILayoutFittingCompressedSize, withHorizontalFittingPriority: UILayoutPriority.required, verticalFittingPriority: UILayoutPriority.fittingSizeLevel) return resizing } 
0
06 . '18 8:41
source share

Swift 4 @mbm29414.

, XIB. , , .

( ) XIB .

sizeForItemAt sizeForItemAt .

 // UICollectionView Vars and Constants let CellXIBName = YouViewCell.XIBName let CellReuseID = YouViewCell.ReuseID var sizingCell = YouViewCell() fileprivate func initCollectionView() { // Connect to view controller collectionView.dataSource = self collectionView.delegate = self // Register XIB collectionView.register(UINib(nibName: CellXIBName, bundle: nil), forCellWithReuseIdentifier: CellReuseID) // Create sizing cell for dynamically sizing cells sizingCell = Bundle.main.loadNibNamed(CellXIBName, owner: self, options: nil)?.first as! YourViewCell // Set scroll direction let layout = UICollectionViewFlowLayout() layout.scrollDirection = .vertical collectionView.collectionViewLayout = layout // Set properties collectionView.alwaysBounceVertical = true collectionView.alwaysBounceHorizontal = false // Set top/bottom padding collectionView.contentInset = UIEdgeInsets(top: collectionViewTopPadding, left: collectionViewSidePadding, bottom: collectionViewBottomPadding, right: collectionViewSidePadding) // Hide scrollers collectionView.showsVerticalScrollIndicator = false collectionView.showsHorizontalScrollIndicator = false } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { // Get cell data and render post let data = YourData[indexPath.row] sizingCell.renderCell(data: data) // Get cell size sizingCell.setNeedsLayout() sizingCell.layoutIfNeeded() let cellSize = sizingCell.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) // Return cell size return cellSize } 
0
14 . '18 21:12
source share



All Articles