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:
- Set
collectionView
's height equal to the expected height of tags. - Set
tagLabel
'sleading
,trailing
,top
andbottom
constraints equal to the corresponding cell'scontentView
constraints. - 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 forestimatedItemSize
. 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:
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:
layout.invalidateLayout()
is unnecessary inViewDidLoad
if let layout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
to setminimumInteritemSpacing
andminimumLineSpacing
is unnecessary, in a statement just above it you have createdlet layout = UICollectionViewFlowLayout()
why dont you simply setminimumInteritemSpacing
andminimumLineSpacing
there itself? Why use additionalif 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
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
EDIT:
As OP is asking for constraints between container view, label and cell, I am updating answer to reflect the same
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):
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
Ios: What's the Fastest, Most Performant Way to Make a Screenshot Programmatically
iOS 7 Sprite Kit Freeing Up Memory
Change Tab Bar Item Selected Color in a Storyboard
How to Reset the iOS Simulator from the Command Line
How to Check for a Null String in Objective-C
Resize Uiimage to 200X200Pt/Px
Toggling Privacy Settings Will Kill the App
Unable to Get the Access Token Through [Fbsdkaccesstoken Currentaccesstoken] in iPhone Sdk
How to Initialize/Instantiate a Custom Uiview Class with a Xib File in Swift
How to Change Locale Programmatically with Swift
How to Use Iboutletcollection to Connect Multiple Uiimageviews to the Same Outlet
How to Get the Console Logs from the iOS Simulator
How to Resize Uiview by Dragging from Its Edges
Autolayout with Hidden Uiviews
How to Obtain a Dynamic Table View Section Header Height Using Auto Layout
Uipopoverpresentationcontroller on iPhone Doesn't Produce Popover