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
Related
What I want to achieve is a layout like this.
Inside to box there will be other content like image, text, button and so on. what I want is make the box sizing dynamic so that it's it takes the height of it's content. The first option came to my mind is use a collection view flow layout.
After some trial and error I end up with this layout.
My Problem is the inter item spacing I want them to be equal, is there any way to achieve this?
Any suggestion I greatly appreciated.
What I have tried so far.
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCollectionViewCell", for: indexPath) as! MyCollectionViewCell
cell.configure(title: data[indexPath.row].title)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let spacing: CGFloat = 10
let numberOfItemsPerRow:CGFloat = 2
let spacingBetweenCells:CGFloat = 10
let totalSpacing = (2 * spacing) + ((numberOfItemsPerRow - 1) * spacingBetweenCells)
let approximateWidthOfContent = view.frame.width - 32
let size = CGSize(width: approximateWidthOfContent, height: 1000)
let attributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15)]
let estimatedFrame = NSString(string: data[indexPath.row].title).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil)
if let collection = self.collectionView{
let width = (collection.bounds.width - totalSpacing)/numberOfItemsPerRow
return CGSize(width: width, height: estimatedFrame.height + 50)
}else{
return CGSize(width: 0, height: 0)
}
}
}
I know there are similar questions but I could not find any which meets my requirement.
After following a tutorial from raywenderlich I finally managed to achieve the layout I wanted, Thanks people who have helped me suggesting with important clues.
What I achieved
The link for the Instructions I have followed is here.
I created a UICollectionViewCell xib and would like the cell to adjust automatically according to the length of the label text like the picture shown below.
The picture shows what I made on a prototype cell. But no matter how I tried, the cell just kept the same size when I used a xib instead.
implement this delegate UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let market = self.markets[indexPath.row]
let label = UILabel(frame: CGRect.zero)
label.text = market
label.sizeToFit()
return CGSize(width: label.frame.width, height: 25)
}
or
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let text = NSAttributedString(string: markets[indexPath.row])
return CGSize(width: text.size().width, height: 25)
}
You can also just implement this in sizeForItemAt
just insert 'yourString' and 'yourFont':
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = yourString.size(withAttributes: [NSAttributedString.Key.font: yourFont]).width
return CGSize(width: width, height: 20)
}
you might need to add some space to the width because of the corner radius of the cell
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)
}
}
I use UICollectionView (views with UIButtons) to book the time:
override func viewDidLoad() {
super.viewDidLoad()
collectionViewTime.register(UINib(nibName: "TimeCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "timecell")
collectionViewTime.dataSource = self
collectionViewTime.delegate = self
if let layout = collectionViewTime.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .vertical
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Timeline.count
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
let numberOfItems = CGFloat(collectionView.numberOfItems(inSection: 0))
let combinedItemWidth = (numberOfItems * flowLayout.itemSize.width) + ((numberOfItems - 1) * flowLayout.minimumInteritemSpacing)
let padding = (collectionView.frame.width - combinedItemWidth) / 2
return UIEdgeInsets(top: 0, left: padding, bottom: 0, right: padding)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "timecell", for: indexPath) as! TimeCollectionViewCell
let time = Timeline[indexPath.row][0]
cell.btnTime.setTitle(DateManager.dateToTime(date: time.0), for: .normal)
return cell
}
And I got all views (UIButtons) in one line:
How can I set automatically moving UIViews in new rows?
So, in my case (iPad) one row will contain 7 UIViews, iPhone will contain 4-5 UIViews in one row.
For that you need to implement UICollectionViewDelegateFlowLayout method collectionView(_:layout:sizeForItemAt:) and change the collectionView's Scroll direction to Vertical
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let minCellSpace: CGFloat = 10 //Set minimum cell space that you want
if UIDevice.current.userInterfaceIdiom == .pad {
//For iPad return 7 cell as you mention in question
return CGSize(width: (collectionView.frame.size.width / 7) - minCellSpace, height: yourCellHeight)
}
else {
//For iPhone return 4 cell, if you want to return 5 cell divide width to 5
return CGSize(width: (collectionView.frame.size.width / 4) - minCellSpace, height: yourCellHeight)
}
}
Please use one more flow delegate method as below
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize;
------
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width:(button width), height:button hieght);
}
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.