iOS - dynamically size width of a UICollectionViewCell - ios

I am just trying to dynamically size cells. I followed a few SO Questions and cannot seem to produce the same results.
Example one
Example two
My code right now is:
extension ItemViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let text = allTags[indexPath.item]
tagName.text = text //label from my custom cell (xCode not recognizing)
return CGRect(width: tagName.intrinsicContentSize.width + 60, height: 40)
}
}
In the second line, tagName is not identified by Xcode. It is part of my custom cell class. The first SO question I linked somehow was able to identify their cell properties within sizeForItemAt. How would I do that to dynamically size my cells? I do have estimated size to non-nil:
if let collectionViewLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
collectionViewLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
}
My desired result in similar to the photos. Let me know if you need more info. Thank you.

Try with This Way.
it's working fine for me in Swift5.
First Implement Collectionview FlowLayout Delegate
extension ItemViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let text = "Your Lable Text"
let font: UIFont = UIFont(name: "Font Name", size: 20) ?? UIFont.systemFont(ofSize: 17.0) // set here font name and font size
let width = text.SizeOf(font).width
return CGSize(width: width + 20.0 , height: 40.0) // ADD width + space between text (for ex : 20.0)
}
}
Add One String Extension.
extension String {
func SizeOf(_ font: UIFont) -> CGSize {
return self.size(withAttributes: [NSAttributedString.Key.font: font])
}
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = UILabel.textWidth(font: UIFont(name: "pass same font name used in label", size: 12.0) ?? UIFont.systemFont(ofSize:
12.0), text: dataArray(indexPath.item))
return CGSize(width: width + 20, height: 50)
}
extension UILabel {
class func textWidth(font: UIFont, text: String) -> CGFloat {
let myText = text as NSString
let rect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
let labelSize = myText.boundingRect(with: rect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
return ceil(labelSize.width)
}
}

Related

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)

issue while set dynamic width of collection view cell

I am trying to dynamically set the width of collection view cell. Initially it's not rendering as expected. But when I tap on the cell, its getting adjusted as I want. Here's the code that I wrote:
Code
import UIKit
class ViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource {
#IBOutlet weak var collView: UICollectionView!
var tasksArray = ["To Do", "SHOPPING","WORK"]
var selectedIndex = Int()
override func viewDidLoad() {
super.viewDidLoad()
let layout = collView?.collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = UICollectionViewFlowLayout.automaticSize
layout.estimatedItemSize = CGSize(width: 93, height: 40)
// Do any additional setup after loading the view, typically from a nib.
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return tasksArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
cell.lblName.text = tasksArray[indexPath.row]
if selectedIndex == indexPath.row
{
cell.backgroundColor = UIColor.lightGray
}
else
{
cell.backgroundColor = UIColor.white
}
cell.layer.borderWidth = 1
cell.layer.cornerRadius = cell.frame.height / 2
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedIndex = indexPath.row
self.collView.reloadData()
}
}
here i am attaching two image before tapping and after tapping so you can easily understood
[![Here is the image before i tap
on cell]2]2
so please tell me whats wrong in my code
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
}
I have found a small trick for swift 4.2
For dynamic width & fixed height:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let label = UILabel(frame: CGRect.zero)
label.text = textArray[indexPath.item]
label.sizeToFit()
return CGSize(width: label.frame.width, height: 32)
}
It is obvious that you have to use sizeForItemAt flow layout delegate in order to pass the dynamic width. But the tricky part is to calculate the width of the cell based on the text. You can actually calculate the width of a text given that you have a font.
Let's introduce few extension which will help us along the way
StringExtensions.swift
extension String {
public func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
let boundingBox = self.boundingRect(with: constraintRect,
options: .usesLineFragmentOrigin,
attributes: [.font: font], context: nil)
return ceil(boundingBox.width)
}
}
This method let us know the width of a string, if i provide it the height and the font. Then use it inside sizeForItem as follows
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let height = 40
let text = YOUR_TEXT
let width = text.width(withConstrainedHeight: height, font: Font.regular.withSize(.extraSmall)) + EXTRA_SPACES_FOR_LEFT_RIGHT_PADDING
return CGSize(width: width, height: height)
}

Cell content disappears after set CellForItemAt indexPath size in CollectionViewFlowLayout?

I'm facing a weird problem. I'm using collectionViewFlowLayout sizeForItem to set cell size based on cell label width and somehow, I'm able to get the correct cell size but my content inside cell disappears. If I just put let say
CGSize(width: 30, height: frame.height)
I will see my label text. My collectionView scrollDirection is .horizontal
Below is my code:
let arrButtons = ["#B1","#ButtonButton2","#Bu3","#Buttontton11","#Buttodasfdsafasdn1","CongViecGiaDinh", "TinCongGiao"]
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let myText = arrButtons[indexPath.item]
let font = UIFont(name: HelveticaNeueFont.HelveticaNeueRegular.rawValue, size: 16)
return CGSize(width: myText.widthOfString(usingFont: font!), height: frame.height)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! CustomCell
cell.data = arrButtons[indexPath.item]
cell.titleLabel.text = arrButtons[indexPath.item]
}
extension String {
func widthOfString(usingFont font: UIFont) -> CGFloat {
let fontAttributes = [NSAttributedStringKey.font: font]
let size = self.size(withAttributes: fontAttributes)
return size.width
}
}
//extension is used to calculate label width
It turns out that I used frame.height instead of collectionView.frame.height. Problem solved!

Swift CollectionViewCell Auto Height

I try to make my cell to auto resize with the content of a label.
My Cell contains two labels. The first label is always one line, the other can be multiline.
The problem is that the cell won't resize. This is my code :
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
CollectionViewHeight.constant = CGFloat(60 + 60 * commentaires.count)
return commentaires.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = commentCollectionView.dequeueReusableCell(withReuseIdentifier: "customCell", for: indexPath) as! CustomCollectionViewCell
cell.contentCommentLabel.text = commentaires[indexPath.row]
cell.nameCommentLabel.text = commentairesNom[indexPath.row]
cell.contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// This is Just for example , for the scenario Step-I -> 1
let yourWidthOfLable = self.view.bounds.size.width
let font = UIFont(name: "System", size: 14.0)
var expectedHeight = heightForLable(text: commentaires[indexPath.row], font: font!, width:yourWidthOfLable )
print("expectedHeight : " + String(describing: expectedHeight))
return CGSize(width: view.frame.width, height: expectedHeight)
}
func heightForLable(text:String, font:UIFont, width:CGFloat) -> CGFloat{
let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.font = font
label.text = text
label.sizeToFit()
return label.frame.height
}
You need to get height of UILabel programmatically according to font family, font size and width of UILabel and than after add this height in top UILabel's height. This is your total height of cell.
Usually i use this code to configure my CollectionView:
private func config_collection()
{
self.collection_view.delegate = self
self.collection_view.dataSource = self
self.collection_view.keyboardDismissMode = .interactive
self.collection_view.alwaysBounceVertical = true
self.collection_view.showsVerticalScrollIndicator = true
self.collection_view.showsHorizontalScrollIndicator = false
if let layout = self.collection_view.collectionViewLayout as? UICollectionViewFlowLayout
{
layout.scrollDirection = .vertical
layout.minimumLineSpacing = 2
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 50)
}
}
Edit 1:
To resize the cell by estimate height i use this code:
func get_size_height_text(texto: String,
ui_view: UIView) -> CGFloat
{
let l : [NSAttributedStringKey : Any] = [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 17)]
let size = CGSize(width: ui_view.frame.width,
height: 1000)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
let estimateFrame = NSString(string: texto).boundingRect(with: size,
options: options,
attributes: l,
context: nil)
return estimateFrame.height
}
To use it, run this code:
let height = get_size_height_text(texto: label.text ?? "",
ui_view: self.view)
with this height, you need to increment the value with your preference to use it in your cell

Resources