Im trying to get my collectionView cells to look like the picture bellow but have not been able to do it. I tried using collectionViewLayout but was not able to get the sizing right along with the proper padding. How would I be able to do this?
Here is my code
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let frameSize = collectionView.frame.size
return CGSize(width: frameSize.width, height: frameSize.height / 2)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 27, bottom: 0, right: 27)
}
}
Here is a picture of what I'm trying to achieve. Thanks
Implement the UICollectionViewFlowLayout's following methods
public func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
public func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
and for the 27px inset use
yourcollectionView.sectionInset = UIEdgeInsets(top: 10, left: 27, bottom: 10, right: 27)
To use you class which implements this protocol, you have to set the collection view's delegate property.
yourcollectionView.delegate = /* object which implement flow layout delegate */
UICollctionViewDelegate automatically conform to UICollectionViewFlowLayoutDelegate, setting the delegate is enough.
You are missing another thing while calculating the cell size. Your cell size calculation is wrong. Implement it as follows
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let frameSize = collectionView.frame.size
let size = (frameSize.width - 64.0) / 2.0 // 27 px on both side, and within, there is 10 px gap.
return CGSize(width: size, height: size)
}
If you are using the storyboard to set layout your UI Widget, you can set them in storyboard too.
Related
I am trying to create 2x2 grid layout using collection view.
I have used below code. It's kind of working fine for iPhone 5 but with tweaks. I am trying to write code that can be used on all screen sizes. This is not working on iPhone 6.
public func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
if let cell1 = cell as? CollectionViewCell {
return cell1
}
return cell
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let padding: CGFloat = 20
let collectionViewSize = collectionView.frame.size.width - padding
return CGSize(width: collectionViewSize/2, height: collectionViewSize/2+15)
}
public func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAt section: Int
) -> UIEdgeInsets {
return UIEdgeInsets(
top: 5, left: 5, bottom: 5, right: 5
)
}
public func collectionView(
_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
I am trying to achieve following layout:
In future this grid can be like 2 columns only and N rows. Each row will always have 2 items.
First of all, get three variables together depending on your needs. Those are the minimum spacing (Both InteritemSpacing and Line spacing - Should be same UXwise but you can change that in delegate methods if you like. The interitem space however must always be equal to minimumSpacing), edgeInsetPadding and the number of items you want in a row.
private var numberOfItemsInRow = 2
private var minimumSpacing = 5
private var edgeInsetPadding = 10
In your code, you have already defined your Edge Insets as:
UIEdgeInsets(
top: 5, left: 5, bottom: 5, right: 5
)
The left and right insets are important for correctly determining the size of the item. left+right gives us a grand sum of 10. This should be assigned to edgeInsetPadding like this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let inset = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
edgeInsetPadding = inset.left+inset.right
return inset
}
Now lets get to your UICollectionViewDelegateFlowLayout. Update the following methods.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return minimumSpacing
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return minimumSpacing
}
Now lets get to the main part. Modify your sizeForItemAt in UICollectionViewDelegateFlowLayout as:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = (Int(UIScreen.main.bounds.size.width) - (numberOfItemsInRow - 1) * minimumSpacing - edgeInsetPadding) / numberOfItemsInRow
return CGSize(width: width, height: width)
}
And this is it. You now get two equal size tiles in 2x2 grid. If you want to change this in future, just change the numberOfItemsInRow variable to something else. Like 3 for 3x3.
This will be done using the item size of the collectionViewCell
collectionView.delegate = self
extension ViewController : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let padding = 5
let width = (collectionView.frame.size.width - CGFloat(padding) * 2) / CGFloat(2)
let height = width / 200 * 110 // or what height you want to do
return CGSize(width: width, height: height)
}
}
Use:
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let padding: CGFloat = 20
let size = self.view.frame.width - padding
return CGSize(width: size/2, height: size/2+15)
}
I have a UICollectionView that can either have a single cell or two cells.
Is it possible for me to horizontally center the first cell and on panLeft have the second cell centered likewise - to the UICollectionView whose width sizes itself to the screen/view size and cell's proportionally adjusting as well?
From the references I've seen, I believe I need to use the UICollectionViewDelegateFlowLayout insetForSectionAt method, but I'm not able to get it exactly centered for some reason, and also when it is just one cell, I believe the inset causes the UICollectionView to become scrollable because enough space was created..
To make collection view cell center, your controller must confirm UICollectionViewDelegateFlowLayout protocol. Use below code to make cells center:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width * 0.7, height: 300)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let width = collectionView.frame.width
let margin = width * 0.3
return UIEdgeInsets(top: 10, left: margin / 2, bottom: 10, right: margin / 2)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return collectionView.frame.width * 0.3 / 2
}
If you return 1 or 2 in numberOfItemsInSection method, It works fine.
I have a problem with a horizontal scroll in collectionView.
I want to scroll by section. I have 3 sections. I want to center it in the view.
First, it looks how I want
But when I scroll next... Left and right insets are different.
The last one is broken too.
I tried it with function scrollViewDidEndDecelerating, but it didn't work. Animation of it was ugly.
My code of collectionView layout.
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)
layout.minimumLineSpacing = 5
layout.scrollDirection = .horizontal
You have to implement UICollectionViewDelegateFlowLayout methods to solve this problem.
let minLineSpace = 4
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: UIScreen.main.bounds.width - minLineSpace, height: collectionView.frame.size.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return minLineSpace
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(0, (minLineSpace / 2), 0, (minLineSpace / 2))
}
Here I have added 4 point spacing between two cells, you can customise this as per your requirement.
here is some snap of it i tried with this code but it's not working:
my collecionview cell :
size is : 166 : 32
Min Spacing : 10 : 10
section insets: 20 : 20
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt
indexPath: IndexPath) -> CGSize
{
if collectionView == MainCollectionView
{
let padding : CGFloat = 50
let collectionViewSize = MainCollectionView.frame.size.width - padding
return CGSize(width: collectionViewSize/2, height: collectionViewSize/2)
}
return CGSize(width: 0, height: 0)
}
Use UICollectionViewDelegateFlowLayout in your view
and call the delegate method
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = self.view.frame.width
return CGSize(width: (width)/2, height: (width)/2) // width & height are the same to make a square cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize{
let width = (collectionView.frame.width/2)-0.5
return CGSize(width:width , height: width)
}
Try implementing these UICollectionViewDelegateFlowLayout delegate methods to achieve the desired result:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
return CGSize(width: (collectionView.bounds.width - 10 - 20*2)/2, height: collectionView.bounds.height)
//In collectionView.bounds.width - 10 - 20*2 :
//10 for cell spacing and
//20*2 for section insets on left and right
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat
{
return 10
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
{
return UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
}
Specify your requirements more clearly so that I can help you further.
Your code is perfect but the delegate method you are using is perhaps not getting called. Based on the swift syntax version you are using, try the latest delegate method. And also make sure that you have made your collection view the observer of that delegate by making it self.
I have a UICollectionView that scrolls horizontally and I would like to have one cell visible on the screen at a time.
I am programmatically setting the cell width and height in the following method:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath as IndexPath) as! Cell
let width = collectionView.bounds.size.width - 40
let height = collectionView.bounds.size.height - 40
cell.bounds.size = CGSize(width: CGFloat(width), height: CGFloat(height))
return cell
}
And after struggling to figure out how to work with insets, spacing, section insets (both programmatically and in the inspectors in XCode) I can't seem to figure out what I need to set and where I need to set it.
Here is a diagram of my desired spacing:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
return CGSize(width: UIScreen.main.bounds.width - 40, height: yourCollectionViewHeight - 40)
}
left 20 - add Footer Size and Header Size in storyboard with width = 20
spacing between cells - add Minimum Spacing in storyboard = 40
I could have made some mistakes and you will not get exactly your layout, but I hope you would get the idea.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width * 0.7, height: 300)
}
func collectionView(_ collectionView: UICollectionView, layoucollectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let margin = collectionView.frame.width * 0.3
return UIEdgeInsets(top: 10, left: margin / 2, bottom: 10, right: margin / 2)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return collectionView.frame.width * 0.3 / 2
}
If you want single cell visible on the screen at a time then important thing is that you should enable paging for your CollectionView. You can do it in swift as below :
collectionView.isPagingEnabled = true
Now conform your ViewController class to UICollectionViewDelegateFlowLayout and implement it's method sizeForItemAt indexPath which returns CGSize for your collectionview cell. For Example:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 200, height: 200)
}