UICollectionViewCell With 2 column grid and dynamic height - ios

I'm trying to have collection view with 2 column but dynamic height.
I have used Autolayout and given required constraints to the Cell
By this way I can calculate the dynamic height but its column grids fails.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! MenuListCollectionViewCell
cell.frame.size.width = collectionView.frame.width/2
cell.menuList = self.menuList[indexPath.row]
let resizing = cell.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize, withHorizontalFittingPriority: UILayoutPriority.required, verticalFittingPriority: UILayoutPriority.fittingSizeLevel)
return resizing
}
This is how I want it to look

Not sure what you mean by "its column grid fails".
Anyway, you need to write a custom collection view layout. The default one (UICollectionViewFlowLayout) allows you to change height of the cells by providing the sizeForItemAt, but that won't change the behavior of the layout that will always arrange cells in rows of the same height (the height of the highest cell).
If I understood correctly, you just want the same layout of this raywenderlich tutorial.
Basically:
Create a subclass of UICollectionViewLayout implementing it's methods:
collectionViewContentSize: return width and height of the collection view content
prepare: where you can calculate the sizes of cells and
collectionView content
layoutAttributesForElements: where you
return an array of UICollectionViewLayoutAttributes in the given
rect
layoutAttributesForItem: where you return the same kind
of attributes, this time specific for an item
assign an object of this class to the collection view layout property

you can use this
This code is somehow written that you can change section inset or minimumInteritemSpacing and this calculate and resize with this parameters
you can use this code or download project from Github
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let numberofItem: CGFloat = 2
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
let collectionViewWidth = self.collectionView.bounds.width
let extraSpace = (numberofItem - 1) * flowLayout.minimumInteritemSpacing
let inset = flowLayout.sectionInset.right + flowLayout.sectionInset.left
let width = Int((collectionViewWidth - extraSpace - inset) / numberofItem)
return CGSize(width: width, height: width)
}

I was able to achieve your desired outcome by doing the following.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 20
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (self.view.frame.size.width/2 - 5), height: 100)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = customCollectionView.dequeueReusableCell(withReuseIdentifier: "test", for: indexPath)
cell.layer.backgroundColor = UIColor.red.cgColor
return cell
}
Make sure you have implemented all the correct delegates
UICollectionViewDelegate
UICollectionViewDatasource
UICollectionViewDelegateFlowLayout
where you see height: make it your own desired height as you have specified in your question.
KEY: Make sure in your storyboard you have set the estimate size of the collection view to NONE -> otherwise the code will not work as expected

Related

How to create dynamic width and static height for UICollectionView using Swift

I am implementing UICollection view with customcell. Here, I need to create dynamic width based on customcell label string and static Height need to set. Here, I used below code for dynamic width don’t know about static height. How to achieve this?
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return (items[indexPath.row] as String).size(withAttributes: nil)
//return CGSize(width: label.frame.width, height: 33.5)
}
}
Calculate the collectionViewCell's width based on the text that you want to add in the label.
Assuming that items is an array of String,
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let textWidth = items[indexPath.row].size(withAttributes:[.font: UIFont.systemFont(ofSize: 20.0)]).width + 30.0 //30.0 is the extra padding
let width = min(textWidth, 100.0) //100.0 is the max width
return CGSize(width: width, height: collectionView.bounds.height)
}
}
Add the attributes and the height as per your requirement.

collection view cell width is not correct

after update to ios 12 and xcode 10 the collectionView with cell sizeing cell does not work correctly anymore.
i have this layout in xcode
the view is like this.
and this is how it looks now.
i have tried to set the collectionview view a widht constraint and modified in the
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
cell.widthConstraint.constant = UIScreen.main.bounds.width / 3
but nothing happens.
You were using the wrong method to set the cells size.
Instead of collectionView(_:cellForItemAt:) use collectionView:layout:sizeForItemAtIndexPath:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let itemHeight = 60
let itemWidth = UIScreen.main.bounds.width / 3
return CGSize(width: itemWidth, height: itemHeight)
}
Also, you can set up items size using Interface Builders Size Inspector, but in that case, the size of your cells will be absolute. If you want it to be 1/3 of screen width, you need to use the layout delegate.

Cells inside UICollectionView do not render properly

I'm new to iOS and Swift, I'm trying to create a UICollectionView for the first time.
Something is not quite right when running what I made. Here's what it looks like in my storyboard:
But when I run:
Top grey area became longer than before and bottoms of the cells seem clipped. I just wrote few lines of code to make the cells' height equal to the collection view's.
let height = collectionView.frame.height
cell.frame.size.height = height
return cell
Any suggestions?
Swift 4 code is here
1) Uncheck the Automatic field of the Row Height in the attribute inspector.
2) Then set the delegate funcitons
extension ViewController : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
var width = CGFloat(80) //change "80" with your cell width
return CGSize(width: width ,height : self.collectionView.frame.height)
}
}
Please use collectionViewLayout to give a size for an item,
class YourController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
and
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let height = collectionView.frame.height
let width = UIScreen.main.bounds.width / 3
let size = CGSize(width: width, height: height)
return size
}
You can make changes from storyboard or from code. I have done from storyboard as shown in image.
And also write this method in your view controller to give a size to your item. For that inherit UICollectionViewDelegateFlowLayout method in your view controller.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let size = collectionView.frame.width / 3
return CGSize(width: size - 20, height: size - 20 )
}
Note: This is just an example. You can set your collection view cell frame.
To change height in a collection view, do not change the cell frame height. There is another delegate for the collection view to set cell height and width.
extension ViewControll : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
return CGSize(width: anyWidth, height: collectionView.frame.size.height)
}
}
Here, anyWidth means whatever width you put. It's up to you

Collectionview and paging

I want to use collection view for show user's instagram photos like Tinder. I placed a collectionview and enabled paging. But i don't know how to show 6 photos for each page. How can i do this?
This is what i want:
First page:
Second Page:
I used all spacing settings zero because setting spacing from storyboard problem for different screen sizes.
My collection view's settings and result:
Result:
So i want to show 6 photos per page. How to achive this?
To show the specific number of cells you want, you have to calculate the size of the cells. Something like this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let numberOfRows = 2
let numberOfColumns = 3
let height = collectionView.frame.size.height / numberOfRows
let width = collectionView.frame.size.width / numberOfColumns
let cellSize = CGSize(width: width, height: height)
return cellSize
}
Since your layout configuration is static you can calculate it just once to make it faster:
class MyVC: UIViewController {
private let cellSize: CGSize = CGSize(width: collectionView.frame.width/numberOfColumns, height: collectionView.frame.height/numberOfRows) // or create a computed property
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return cellSize
}
}
This code is untested, maybe you have to add some padding but you get the idea. Another option would be to implement your own UICollectionViewLayout subclass

Swift, Change width of UICollectionViewCell and UILabel(inside the cell) programmatically

I've set the width of a cell(UICollectionViewCell) to be equal to the width of the UICollectionView and I'm trying to do exactly the same thing with the UILabel that is included inside that cell. I think the code below explains exactly what I'm trying to achieve. So i've read some question here in SO and also a couple of tutorials but I'm still not sure how I can achieve this.
In a couple of questions it was saying about using collectionViewLayout but I'm really struggling on how to use it within my code. Any ideas? Thank you!
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("myCell", forIndexPath: indexPath) as LocationViewCell
cell.locationLabel.text = "Hello there!"
// Set cell & label width to 100%
let collectionViewWidth = self.collectionView.bounds.size.width
cell.frame.size.width = collectionViewWidth // Works
cell.locationLabel.frame.size.width = collectionViewWidth // Does NOT
Update 1
So I added the following:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
// Set cell width to 100%
let collectionViewWidth = self.collectionView.bounds.size.width
return CGSize(width: collectionViewWidth, height: 35)
}
What happens is that when the view is loaded the UILabel's width is still small. If I go to another view and then return back then it's 100%. So I have to do something in the viewDidLoad() right? I'm already using self.collectionView.reloadData() but I guess that's only for data.
Update 2
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("locationCell", forIndexPath: indexPath) as LocationViewCell
cell.locationLabel.text = "Hello UILabel"
// Set cell width to 100%
let collectionViewWidth = self.collectionView.bounds.size.width
cell.frame.size.width = collectionViewWidth
cell.locationLabel.frame.size.width = collectionViewWidth
return cell
}
It doesn't work because by the time this method is called, the collection view already knows how big the cell should be because it has got it from the flow delegate method:
optional func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
This is where you should be setting the size of your cells.

Resources