How to set collectionview cell width based on content? - ios

I have a horizontal scrolling collectionview.The cell contains a label.I want to adjust the cell width based on the content of the label.How can this be achieved??Please help me with this

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)

Use this func to calculate the label width
func widthOfString(usingFont font: UIFont) -> CGFloat {
let fontAttributes = [NSFontAttributeName: font]
let size = self.size(attributes: fontAttributes)
return size.width
}
Use UICollectionViewDelegateFlowLayout to set size of collectionViewCell
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = //calculated width of label from func
return CGSize(width: width, height:requiredHeight)
}

Related

cellForItemAt not called if numberOfItemsInSection is 1 UICollectionView

Following is my code which is working fine if numberOfItemsInSection is greater than 1 but if numberOfItemsInSection is equal to 1 it should display single cell but it is not happening, for numberOfItemsInSection equals 1 noting is displayed and cellForItemAt is not called
Also initial numberOfItemsInSection is Zero after making api call and receiving data it becomes one and I call following code as well
tabsCollectionView.reloadData()
Other code
tabsCollectionView.delegate = self
tabsCollectionView.dataSource = self
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return menuResult?.foodMenuItems?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = tabsCollectionView.dequeueReusableCell(withReuseIdentifier: "foodTabsCollectionViewCellID", for: indexPath) as! FoodTabsCollectionViewCell
cell.foodMenuItem = menuResult?.foodMenuItems?[indexPath.row]
cell.setUI()
return cell
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
following code related sizing in side viewDidLoad()
if let flowLayout = tabsCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.estimatedItemSize = CGSize(width: 200, height: 70)
flowLayout.itemSize = UICollectionViewFlowLayout.automaticSize
tabsCollectionView.collectionViewLayout = flowLayout
}
sizeForItemAt method
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
var height = collectionView.frame.height
var width = collectionView.frame.width
if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
width = flowLayout.estimatedItemSize.width
height = flowLayout.estimatedItemSize.height
}
return CGSize(width: width, height: height)
}
Following is code inside FoodTabsCollectionViewCell
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
setNeedsLayout()
layoutIfNeeded()
let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
var frame = layoutAttributes.frame
frame.size.width = ceil(size.width)
frame.size.height = ceil(size.height)
layoutAttributes.frame = frame
layoutIfNeeded()
return layoutAttributes
}
I had the same problem when I tried to set a flexible width to a cell containing a UILabel with variable width (actually the size was related to the width of the text of the UILabel)
So initially I was using this approach (THIS was causing the problems):
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize <--- THIS
layout.itemSize.height = 70 <-- THIS
Then I removed the automaticSize (and itemSize.height) as below:
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
And it worked.
To make sure that I had flexible cell width I used this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let dummyCell = MyCell(frame: .init(x: 0, y: 0, width: frame.width, height: 40))
dummyCell.name = users[indexPath.item].name
// 3. Force the view to update its layout
dummyCell.layoutIfNeeded()
// 4. Calculate the estimated size
let estimatedSize = dummyCell.systemLayoutSizeFitting(.init(width: frame.width, height: 40))
return CGSize(width: estimatedSize.width, height: 40)
}
The problem will happen when the collectionView scroll direction is horizontal and the height of collectionView is less than 50. I had the same problem and I increased the collectionView height to 50 then the problem solved.

Autolayout is broken when UILabel given trailing

It works fine if I don't give trailing properties.
However, if the text is long, it will be cut off the screen.
So I set trailing.
But, this will break the width of the UICollectionViewCell.
This is my code
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.width
return CGSize(width: width / 2, height: 53)
}
Why is that? Please help me.
Simplest method is just set label lines as 0
#oddK - Don't set the width as collection width / 2, instead set the width based on the text content,
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.width
let lblWidth = "My text".getWidth(withConstrainedHeight: CGFloat(53), font: UIFont.systemFont(ofSize: 14))
return CGSize(width: lblWidth, height: 53)
}
//Get Width of content in label
func getWidth(withConstrainedHeight:CGFloat, font: UIFont) -> CGFloat {
let viewRect = CGSize(width: .greatestFiniteMagnitude, height: withConstrainedHeight)
let getFrame = self.boundingRect(with: viewRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font : font], context: nil)
return getFrame.size.width
}

Generating FlexBox layout with Button

How can generate the flexbox layout with button please check the attach image. Any library available? Or can make with collectionView.
Yes you should use a collection view, and implement this delegate function
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
var str: String = data[indexPath.row]
let font: UIFont = /*Your Font*/
let size: CGSize = str.size(withAttributes: [NSAttributedString.Key.font: font])
return CGSize(width: size.width + 39, height: 45)

Dynamic Width Cell in UICollectionView

I try to implement labels with UICollectionView. But I have to set the widths with dynamic labels length. I try to remove gaps in picture. I share some code. I use Nibs.
Thanks in advance.
layoutFCV.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
let filterCV = UICollectionView(frame: self.view.bounds, collectionViewLayout: layoutFCV)
--
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if collectionView == filterCollectionView {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FilterSelectionCollectionViewCell", for: indexPath) as! FilterSelectionCollectionViewCell
return CGSize(width: cell.title.frame.width , height: self.filterCollectionView.frame.height)
} else {
return CGSize(width: 10, height: 10)
}
}
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)
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return "String".size(withAttributes: [
NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 14)
])
}
}
First make sure your constraints in UICollectionViewCell.xib file has been set correctly. I mean with the growth of UILabel in cell, the cell itself should grow as well.
You should explicitly set the title before you get the cell size. So here is your collectionView(collectionView:, layout:, sizeForItemAt:) method should be:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if collectionView == filterCollectionView {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FilterSelectionCollectionViewCell", for: indexPath) as! FilterSelectionCollectionViewCell
cell.title.text = "Newest" //this is for the "Newest" cell. Of curse you should set the proper title for each indexPath
cell.setNeedsLayout()
cell.layoutIfNeede()
return CGSize(width: cell.contenView.frame.width , height: cell.contentView.frame.height)
} else {
return CGSize(width: 10, height: 10)
}
}

Uicollectionview cell width issue iOS 10

I have a collection view controller and I set up it like this:
class r : UICollectionViewCell {
override var bounds: CGRect {
didSet {
contentView.frame = bounds
}
}
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "1", for: indexPath) as? r
cell.backgroundColor = UIColor.red
return cell
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
I want that the cell's width is equals to screen's width , for this reason in storyboard I edited cell's width:
but I have a problem :
if I execute it on iPhone 6 simulator I get this(and this is what I want to get on all devices) :
but If execute on iPad Pro I get this :
And I don't understand why.
Can you help me?
P.S I don't want to use tableview for some reasons
In iOS 10 there is a minor difference in function prototype:
Here is Swift3.0 Delegate method for sizeAtItemAtIndexPath:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
let screenRect: CGRect = UIScreen.main.bounds
let screenWidth: CGFloat = screenRect.size.width
let screencellHeight: CGFloat = screenRect.size.height
let cellWidth: CGFloat = screenWidth / CGFloat(2)
let cellHeight:CGFloat = screencellHeight / CGFloat(10.0)
let size: CGSize = CGSize(width: cellWidth, height: cellHeight)
return size
}
If u use Constraint so first of all your cell to apply equal width constraint and also storyboard in you should check collection view setting in inspect editor for horizontal and vertical.proper all setting..
I assume you have set a fixed width for your cell somewhere. Maybe as constraint. Set it dynamically:
Conform to UICollectionViewDelegateFlowLayout
and override:
#objc(collectionView:layout:sizeForItemAtIndexPath:)
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let desiredNumberOfCollumns: CGFloat = 1
let width = collectionView.frame.width / desiredNumberOfCollumns
let height = 100 // your desired height
return CGSize(width: width, height: hight)
}
I think you are using constraints. If yes, then all you have to do is just fix the width of your cell in constraints.
Unless you didn't change that. No matter where you have set the width ind delegates.It will remain same.

Resources