I want dynamic cell Size as per text in TextView,
Here is my code
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return DataArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? CustomCollectionViewCell
let fixedWidth = cell?.TextView.frame.size.width
cell?.TextView.sizeThatFits(CGSize(width: fixedWidth!, height: CGFloat.greatestFiniteMagnitude))
let newSize = cell?.TextView.sizeThatFits(CGSize(width: fixedWidth!, height: CGFloat.greatestFiniteMagnitude))
var newFrame = cell?.TextView.frame
newFrame?.size = CGSize(width: max((newSize?.width)!, fixedWidth!), height: (newSize?.height)!)
cell?.TextView.frame = newFrame!
TextViewFrame = newFrame!
cell?.TextView.text = DataArray[indexPath.row] as? String
return cell!
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
return CGSize(width: CV.frame.size.width, height: 200)
}
Here i have given 200 fixed size for cell. it gives me output as below
It gives me cell height 200 for small content as well as for large content .
But actually i want cell height as per content.
How can i achieve this?
Any help is appreciated. Thanks in advance
You can find the height of the cell once you know the bounding rect for the text you want to display.
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let text = "String that you want to show in the label inside the cell"
let boundingRect = NSString(string: text).boundingRectWithSize(CGSizeMake(collectionView.bounds.width, 1000),
options: .UsesLineFragmentOrigin,
attributes: [NSFontAttributeName: UIFont.systemFontOfSize(11.0)],
context: nil)
let size = CGSizeMake(collectionView.bounds.width, ceil(boundingRect.height))
return size
}
Just be sure to make up for the top/bottom/left/right margins of your label to the cell.
Related
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)
}
}
Right now, i'm facing an issue.
I manage to wrap the cell's content view to its label, but there's some spacing which doesn't fit to the design. There's some spaces available between the cell
Here's my code
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 4
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let hastag = Hastag.fetchHastag()[indexPath.row]
let approximateWidth = view.frame.width - 20
let size = CGSize(width: approximateWidth, height: 28)
let attribute = [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 12)]
let estimatedFrame = NSString(string: hastag.hastag).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attribute, context: nil)
return CGSize(width: estimatedFrame.width + 20, height: 26)
}
Below is the result
And this is what i want to achieve, it will be something like this
UPDATE
Here is my full code of collection view data source and delegate. Hastag.hastag is type of string
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Hastag.fetchHastag().count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "hastagCell", for: indexPath) as! HastagCollectionViewCell
cell.hastag = Hastag.fetchHastag()[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 4
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let hastag = Hastag.fetchHastag()[indexPath.row]
let approximateWidth = view.frame.width - 20
let size = CGSize(width: approximateWidth, height: 28)
let attribute = [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 12)]
let estimatedFrame = NSString(string: hastag.hastag).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attribute, context: nil)
return CGSize(width: estimatedFrame.width + 16, height: 26)
}
You can use AlignedCollectionViewFlowLayout, its not a library just one file. Just add this file to your project and from your UICollectionView xib just set the flow layout as AlignedCollectionViewFlowLayout. It is very easy to use. Please check the below link.
https://github.com/mischa-hildebrand/AlignedCollectionViewFlowLayout
You can use the size(withAttributes:) function of a NSString to give you the CGSize of a string with a specific font and size.
Assuming that Hastag.hastag is a String I think the following should work:
extension YourViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let hastag = Hastag.fetchHastag()[indexPath.row]
let title = hastag.hastag as NSString
let titleSize = title.size(withAttributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 12)])
return CGSize(width: titleSize.width + 20, height: 26)
}
}
Note that your ViewController must implement the UICollectionViewDelegateFlowLayout protocol and you must set your ViewController as the delegate for the UICollectionView for this to work.
Hope that helps.
After several try, i finally give up on customizing cell size and instead i'm using the library TagListView
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!
I am trying to edit the width of the cell so that no matter what screen size, I will have score1.count cells in a row. The code I am using for my Collection View is the following:
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! MyCollectionViewCell
cell.myLabel.text = String(self.items[indexPath.item])
return cell
}
This is the function targeting the width of the cell.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,sizeForItemAt indexPath: IndexPath) -> CGSize {
let size = Int(self.view.frame.size.width)/(score1.count + 1)
return CGSize(width: CGFloat(size), height: 80)
}
I have spent many hours looking at different sources and trying different things, but the collection view never has the right number of cells. One idea is that it may be because I have the cell size set in auto layout but whenever I go to change it xcode crashes. Any ideas on bypassing the auto layout? Or how to change the auto layout?
Thanks!
try this
extension YourViewController : UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let rowItems:CGFloat = 4
let padding:CGFloat = 2
let width = (collectionView.bounds.width / rowItems) - padding //Change width as per your requirement
let height = collectionView.bounds.height - (2 * padding)//Change height as per your requirement
return CGSize(width: width, height: height)
}
}
You can change your sizeForItemAtIndexPath as. You can assign size of cell after substracting margin and diving by number of cell.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,sizeForItemAt indexPath: IndexPath) -> CGSize {
let size = (Int(collectionView.bounds.width) - (score1.count+1)*5) /(score1.count) // 5 is the margin you want to keep in between 2 cells
return CGSize(width: CGFloat(size), height: 80)
}
Do let me know if you have any queries. I would try and clarify.
I have created an UICollectionView horizontal scroll through programmatically. UICollectionView cell width is dynamically created based on the content.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath as IndexPath) as! CustomCollectionViewCell
cell.label.text = dataSource[indexPath.item]
cell.label.font = UIFont(name: "Helvetica", size: 20.0)
if currentPageIndex == indexPath.item {
cell.currentPageIndicator.frame = CGRect(x: 0, y: cell.bounds.height - 2, width: cell.bounds.width, height: 2.5)
cell.currentPageIndicator.isHidden = false
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if let size = cellSizeCache.object(forKey: indexPath as AnyObject) as? NSValue {
return size.cgSizeValue
}
let string = dataSource[indexPath.item]
let height = collectionView.frame.size.height
let font = UIFont(name: "Helvetica", size: 20.0)
var size = string.boundingRect(with: CGSize(width: CGFloat.infinity, height: height), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName: font!], context: nil).size
size.width += 20
size.height += 20
return size
}
Initially, an UICollectionView is looking fine, even when I scroll horizontally.
When I start scrolling fast, cell's are overlap with each other.
Your cells are overlapping because the width of the cell is lesser then the size of content. You have two options, first option is to increase the width of cell in storyboard. Second option is to set the size for each cell in collectionview method named "sizeforcellatindexpath", sample method is given below:
optional func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return size
}
you can calculate the size of string by using calculation mechanism. Sample for collection in given below:
How to calculate the width of a text string of a specific font and font-size?