UICollectionView cell fixed width and dynamic height using constraints in swift - ios

I have one collectionview and a cell inside that which is having a fixed width and dynamic height based on its contents.I am using autolayout constraints , my code is below.
I have already tried to set all types of auto constraints inside the cell and inside the view .
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! ViewControllerCell
cell.lbl_text.text = "djs sdfs sdfs dsfsfd gdgfd dfgd df "
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if let flowLayout = coll_main?.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
}
}
In cell class method:
override func awakeFromNib() {
super.awakeFromNib()
self.contentView.autoresizingMask = [UIView.AutoresizingMask.flexibleHeight]
}
override func layoutSubviews() {
self.layoutIfNeeded()
}
i get cell width fix without i am set that width. how may this issue solve?
here screenshots.

You need to make sure that your Estimate Size is set to None for your CollectionView.

Please do the following steps
Select your collectionView in interface builder.
Go to the size inspector
Change estimated size to none as shown in the image.
Hopefully it will fix the problem

You can use collectionView delegate
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width:, height:)
}
for this you need to extend UICollectionViewDelegateFlowLayout

You have to use UICollectionViewDelegateFlowLayout and implement sizeForItemAt like following.
// MARK: - UICollectionViewDelegateFlowLayout -
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let sectionInset = (collectionViewLayout as! UICollectionViewFlowLayout).sectionInset
// Here you can set dynamic height for the individual cell according to your content size.
let referenceHeight: CGFloat = 100 // Approximate height of your cell
let referenceWidth = collectionView.safeAreaLayoutGuide.layoutFrame.width
- sectionInset.left
- sectionInset.right
- collectionView.contentInset.left
- collectionView.contentInset.right
return CGSize(width: referenceWidth, height: referenceHeight)
}
Here is an articale about it, you can follow this.

Add this property in your collection view or uncheck PrefetchingEnabled property in your nib.
collectionView.isPrefetchingEnabled = false

Implement UICollectionViewDelegateFlowLayout method for collection cell size.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
Then go to the storyboard and in size inspector make collectionView Estimate size to None

Related

CollectionView is not displaying vertically as expected

I am trying to implement following screen.
But in my code, it is displaying following way
following code I wrote,
import UIKit
class LunchViewController: UIViewController {
#IBOutlet weak var mainCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
extension LunchViewController:UICollectionViewDataSource,UICollectionViewDelegateFlowLayout,UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 412, height: 180)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// return self.myLibraryArray.count
return 8
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// let item = self.myLibraryArray[indexPath.row]
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RestaurantCollectionViewCell", for: indexPath) as? RestaurantCollectionViewCell else {
return UICollectionViewCell()
}
//cell.fillDetails(item)
return cell
}
}
Here is the complete project link
Can any one tell me, what is the issue? or what I have to write the code to get expected output?
Update:
Select collectionView and Change Estimate Size to none. In storyboard.
if you dont want space between two cells then set 0 to for lines in Min spacing
And i have given width as screen width
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: UIScreen.main.bounds.size.width, height: 180)
}
Output :-
Change sizeForItem method to this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.size.width, height: 180)
}
Also, make sure the mainCollectionView is constrained to the leading and trailing edges in the storyboard.

UICollectionViewCell is wrong width with label [duplicate]

This question already has answers here:
UICollectionViewLayout Not working with UIImage in Swift 5 and Xcode 11
(2 answers)
Closed 2 years ago.
I found something strange.
I created a custom UICollectionViewCell and a label inside it.
I applied auto-layout only to the top, leading, and bottom of the label.
class TestCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var label: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
}
And if the text is long, it is clipped off the screen like below.
I want the text to be displayed as just "..." if it is too long.
So I applied traling auto-layout like this.
Then, collectionViewCell width is broke.
This is my code.
I already set delegate, datasource.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 20
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? TestCollectionViewCell else { return UICollectionViewCell() }
cell.label.text = "asdfasdkfjabsdkljfbasdfhaoweihf;oaisefowiejfao;wihfaksdjfhakdjf;lskdjfa;zxknb"
return cell
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (collectionView.frame.width - 1) / 2, height: 53)
}
}
Open Storyboard and select your CollectionView.
Now open Size inspector and change Estimated size Automatic to None.
Hope this will work.
Try calculating size of cell as:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (UIScreen.main.bounds.width - 1) / 2, height: 53)
}
You can set the size of the cell using UICollectionViewFlowLayout()
In your viewDidLoad
override func viewDidLoad() {
collectionView.dataSource = self
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: (collectionView.frame.width - 1) / 2, height: 53)
collectionView.setCollectionViewLayout(layout, animated: true)
}

Swift UICollectionView Horizontal scroll cell label width change dynamically based on label content length

I am trying to create UICollectionView Horizontal scrolling (like a top menu single row) with cell label. Here, I need to implement my label content different length so based on different length cell label should auto adjust Its width. I am using storyboard for my design.
Below my code
extension CameraViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
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: Constants.reuseID, for: indexPath) as! cameraModeCollectionViewCell
cell.cameraMode.text = self.items[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.item)!")
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(0, 40, 0, 40)
}
}
Current output Iam getting
Implement UICollectionViewDelegateFlowLayout's method collectionView(_:layout:sizeForItemAt) and calculate the width of collectionViewCell based the width of text that you're populating the label with.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let text = self.items[indexPath.row]
let cellWidth = text.size(withAttributes:[.font: UIFont.systemFont(ofSize: 14.0)]).width + 10.0
return CGSize(width: cellWidth, height: collectionView.bounds.height)
}
In the above code I've used self.items[indexPath.row] as the source of text. 10.0 is the extra padding added to the cell.
In case the items array is optional, do unwrap the value of self.items[indexPath.row] before using.
Step 1 : Find the width of the text that has to be placed in each cell. This can be found using below function
string.size(withAttributes:[.font: UIFont.systemFont(ofSize: 14.0)]).width
Using this function we can find width of dynamic text during run time.
Step 2: Set the size of each cell using the width that we found in Step 1. Size of each cell is set using below delegate
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
Combining Step 1 and 2, we can get a collection view which has cell width as per text width that is placed inside it.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let text = self.items[indexPath.row] // getting text of each index
let cellWidth = text.size(withAttributes:[.font: UIFont.systemFont(ofSize: 14.0)]).width + 10.0 // getting width of each text + extra margin of 10
return CGSize(width: cellWidth, height: 40.0) // height as per your requirement
}

Editing the width of a CollectionViewCell

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.

UICollectionView cells overlapping swift

I am creating a UICollectionView, but when a new cell is added to the view it is partially overlapping the previous cell. Below is my code:
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CustomCell", forIndexPath: indexPath) as! CustomCell
cell.frame.size.width = self.view.frame.width / 3
cell.frame.size.height = self.view.frame.height / 4
cell.backgroundView = imageView
return cell
}
How do I make sure the cells don't overlap?
You shouldn't assign the frame value by yourself in the cellForItemAtIndexPath method.
The more suitably way is to do this in the UICollectionViewLayout, then set it as the collectionview's layout property. Actually, you need a UICollectionViewLayout instance when you init the UICollectionView instance.
Or simply, use the UICollectionViewFlowLayout which system provides to implement the flow layout conveniently, tell it the size of each cell, spaces between them, and some other informations by its delegate. The layout instance will arrange all for you.
For example.
class MYViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
//In viewDidLoad()
var collectionViewFlowLayout = UICollectionViewFlowLayout()
var myCollectionView = UICollectionView(frame: self.view.bounds, layout: collectionViewFlowLayout)
// A flow layout works with the collection view’s delegate object to determine the size of items, headers, and footers in each section and grid.
// That delegate object must conform to the UICollectionViewDelegateFlowLayout protocol.
myCollectionView.delegate = self
myCollectionView.dataSource = self
// MARK: UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(10, 10);
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
return 10;
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
return 10;
}
}
For more infomation, read the doc.
For me it was the wrong cell width and height specified in the size inspector of the view controller. Just set that properly possibly the same as your nib file and it may work.
Please see screenshot for clarity.
All what you need is to extend you ViewController from UICollectionViewDelegateFlowLayout and implement sizeForItemAt :
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if (collectionView == myCollectionView_1) {
return CGSize(width: self.view.frame.width / 3, height: self.view.frame.height / 4)
} else {
return collectionView.frame.size
}
}
For me, simply changing Estimated Size to None at the collection view options fixed the problem.
For ref please check the below screenshot:

Resources