Dynamic Cell Width of Uicollectionview Depending on Label Width

Dynamic cell width of UICollectionView depending on label width

In sizeForItemAtIndexPath return the size of the text

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{

return [(NSString*)[arrayOfStats objectAtIndex:indexPath.row] sizeWithAttributes:NULL];
}

issue while set dynamic width of collection view cell

Inside your CollectionViewCell override preferredLayoutAttributesFitting function This is where the cell has a chance to indicate its preferred attributes, including size, which we calculate using auto layout.

 override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
setNeedsLayout()
layoutIfNeeded()
let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
var frame = layoutAttributes.frame
frame.size.width = ceil(size.width)
layoutAttributes.frame = frame
return layoutAttributes
}

Dynamic Width Cell in UICollectionView

Use this code:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let text = "Title"
let width = self.estimatedFrame(text: text, font: UIFont.systemFont(ofSize: 14)).width
return CGSize(width: width, height: 50.0)
}

func estimatedFrame(text: String, font: UIFont) -> CGRect {
let size = CGSize(width: 200, height: 1000) // temporary size
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
return NSString(string: text).boundingRect(with: size,
options: options,
attributes: [NSFontAttributeName: font],
context: nil)
}

How to dynamically size CollectionViewCell as per UILabel

You don't need to calculate the size of the strings that you want to display. Do the following:

  1. Set collectionView's height equal to the expected height of tags.
  2. Set tagLabel's leading, trailing, top and bottom constraints equal to the corresponding cell's contentView constraints.
  3. Remove your implementation of func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize. The cell size will be calculated automatically if you don't use a custom value for estimatedItemSize. If you want to be sure, add (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.estimatedItemSize = UICollectionViewFlowLayout.automaticSize to the function where you setup all views.

You will see the following result:

Running on iPhone 11 Pro Max simulator

How to change CollectionView cell width according to label text in swift

All items in your collectionView are taking the exact same size because you have specified layout.itemSize for your collectionView's flow layout

remove layout.itemSize = CGSize(width: self.collectionView.frame.size.width / 2, height: 80)

Other issues in code:

  1. layout.invalidateLayout() is unnecessary in ViewDidLoad
  2. if let layout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout { to set minimumInteritemSpacing and minimumLineSpacing is unnecessary, in a statement just above it you have created let layout = UICollectionViewFlowLayout() why dont you simply set minimumInteritemSpacing and minimumLineSpacing there itself? Why use additional if let?

Simply write

        let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 5
self.collectionView.collectionViewLayout = layout

You need not specify item size, Collection view flow layout is set to be horizontal in nature, so by default each item will take the same height as the collection view height and if you have only one label inside the cell and if you have set autolayout constraint properly, label's intrinsic content size will ensure cell gets proper width

Sample Image

And forwhatever reason if you decide to wrap the label inside a UIView logic above holds good, as long as you set the label's autolayout constraint right, lablel's intrinsic content size will set the width of the containerView and which will in turn contribute to set the cell's width appropriately,

Here cell has background color of red, container view has orange and label has no background color, I have added leading and trailing spacing between cell and view as 10, and label and container view has leading and trailing space as 5

Sample Image

EDIT:

As OP is asking for constraints between container view, label and cell, I am updating answer to reflect the same

Sample Image

Clearly

  - - - - - - - - - - - - Cell - - - - - - - - - - - - - - - -
| - - - - - - - - Container View - - - - - - - - |
| | |5 | |
|-10- |-5- Label -5-| -10- |
| | |5 | |
| - - - - - - - - - - - - - - - - - - - - - - - - - |
| |
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

You can add top and bottom constraint between cell and conatiner if you need, but I have kept container view vertically center to cell

How it works?

Label has intrinsic content-size which it gets from the text set to it, View does gets its intrinsic size based on the size of its subview, because there are no subviews in your container view, it gets its size from label and clearly whatever the size label returns view adds 10 to width and 10 to height ( 5 leading, 5 trailing, 5 top and 5 bottom) finally cell gets its size from its subviews, because there is only one subview (container view) it gets width from container view and for height it gets its height from collection view (as subviews dont provide enough data to calculate its height)

And if for some reason (might be because of some other constraints you have set knowingly / unknowingly like adding width constraint on container view) label is not taking its intrinsic content size correctly, you can always set setContentCompressionResistancePriority and setContentHuggingPriority on label to high in your cell's awake from nib

override func awakeFromNib() {
super.awakeFromNib()
self.label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
self.label.setContentHuggingPriority(.defaultHigh, for: .horizontal)
}

Though this shouldnt be needed in usual case, if this is needed to get correct intrinsic size, there must be some other constraints which are preventing the label to take its proper implicit intrinsic content size, if I were in your position would rather check and fix it first. Nonetheless cant debug further with limited code you have provided.

EDIT 2:

Did not realize that OP is using custom instance of UICollectionViewFlowLayout and has not set estimatedItemSize property to automaticSize

        let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 5
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
self.collectionView.collectionViewLayout = layout

UICollectionView: Apply dynamic width for each cell according to UIButton's text width

Few things wrong...

Get rid of sizeForItemAtIndexPath method -- you want to use auto-sizing cells.

Next, you don't show your code for constraining the "container" view, but I assume it's something like this:

- (void)viewDidLoad {
[super viewDidLoad];

menuBarView = [MenuBarView new];

menuBarView.translatesAutoresizingMaskIntoConstraints = NO;

[self.view addSubview:menuBarView];

UILayoutGuide *g = [self.view safeAreaLayoutGuide];
[NSLayoutConstraint activateConstraints:@[
[menuBarView.leadingAnchor constraintEqualToAnchor:g.leadingAnchor constant:0.0],
[menuBarView.trailingAnchor constraintEqualToAnchor:g.trailingAnchor constant:0.0],
[menuBarView.topAnchor constraintEqualToAnchor:g.topAnchor constant:4.0],
[menuBarView.heightAnchor constraintEqualToConstant:42.0],
]];

}

Your UICollectionViewFlowLayout should be along these lines:

UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;

// spacing between cells
layout.minimumLineSpacing = 8.0;

// prevents last cell from being cut-off
layout.minimumInteritemSpacing = layout.minimumLineSpacing;

// must have an estimated size
// inconsequential, but needs to be less than expectd
layout.estimatedItemSize = CGSizeMake(10, 10);

And configure your cell class like this:

// I don't know what your super-class is...
//[super setViews];

self.contentView.backgroundColor = UIColor.systemYellowColor;

// create buttons
_button = [UIButton buttonWithType:UIButtonTypeCustom];
_button.translatesAutoresizingMaskIntoConstraints = false;
_button.layer.cornerRadius = 15;
_button.layer.masksToBounds = true;

// give the button a little top / leading / bottom / trailing "padding"
_button.contentEdgeInsets = UIEdgeInsetsMake(8, 20, 8, 20);

[_button setBackgroundColor:[UIColor colorWithWhite:0.92 alpha:1]];
[_button setTitleColor:UIColor.labelColor forState:UIControlStateNormal];
[_button setTitleColor:UIColor.whiteColor forState:UIControlStateHighlighted];

[self.contentView addSubview:_button];

// set constraints
UIView *g = self.contentView;
[NSLayoutConstraint activateConstraints:@[
[_button.leadingAnchor constraintEqualToAnchor:g.leadingAnchor constant:0.0],
[_button.trailingAnchor constraintEqualToAnchor:g.trailingAnchor constant:0.0],
[_button.topAnchor constraintEqualToAnchor:g.topAnchor constant:2.0],
[_button.bottomAnchor constraintEqualToAnchor:g.bottomAnchor constant:-2.0],
]];

Result should look like this (I gave the "container" view a blue background so we can see it):

Sample Image

How to set collectionview cell width based on content?

instead of calling sizeforitem

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

return CGSize(width: 100, height : 100)
}

You can set layout properties to auto resizing according to label content

let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
layout.estimatedItemSize = CGSize(width: 100, height: 30)

how to make dynamic width of the UICollectionViewCell

You can try this...

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout*) collectionView.collectionViewLayout;

if (indexPath.item % 3 == 0) {
float cellWidth = (CGRectGetWidth(collectionView.frame) - (flowLayout.sectionInset.left + flowLayout.sectionInset.right));
return CGSizeMake(cellWidth, cellWidth / 2);
} else {
float cellWidth = (CGRectGetWidth(collectionView.frame) - (flowLayout.sectionInset.left + flowLayout.sectionInset.right) - flowLayout.minimumInteritemSpacing) / 2;
return CGSizeMake(cellWidth, cellWidth);
}
}

UiCollectionView cell dynamic width

You need to get the attributes for the label then create an NSAttributedString with the data for the give index path. NSAttributedString has a size method which will tell you how big the text should be.

It looks like your label has no attributes and no data so assuming you want the cell to be the actual size of your label with zero padding its:

let text = NSAttributedString(string: "deneme Tag")
return text.size()


Related Topics



Leave a reply



Submit