I'm new to swift , i want to make a UICollectionViewCell layout to be dynamic example: if we have an item added to look like this:
1.if we have one item it look like this
2.If we have two items added to look like this:
3. if we have item 3 again to look like this :
This process proceeds this way, as long as there are items
Thanks!
This is method of UICollectionViewDelegateFlowLayout for the size of items.
Just check for total item count odd/even and based on that decide you cell width.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if arrList.count % 2 == 0 {
let width = (collectionView.frame.width-20)/2
let height : CGFloat = 100.0
return CGSize(width: width, height: height)
}else {
if arrList.count-1 == indexPath.row {
let width = collectionView.frame.width-10
let height : CGFloat = 100.0
return CGSize(width: width, height: height)
}else{
let width = (collectionView.frame.width-20)/2
let height : CGFloat = 100.0
return CGSize(width: width, height: height)
}
}
}
This is interspacing methods.
//these methods are to configure the spacing between items
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(0,5,5,5)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
Related
I am getting the space between the cells (marked with blue color):
extension ProfileVC: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if collectionView == self.humanCollectionView {
let screenWidth = humanCollectionView.bounds.width
return CGSize(width: screenWidth/3-0, height: screenWidth/3-0)
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 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
}
Why are you declaring the delegate functions inside collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, sizeForItemAt: IndexPath) delegate method?
Try to do like this:
import UIKit
class ProfileVC: UICollectionViewController {
override func viewDidLoad() {
super.viewDidLoad()
//remember to set the delegates
self.collectionView.delegate = self
self.collectionView.dataSource = self
//this is optional just to test that it is working
collectionView.backgroundColor = .red
// Do any additional setup after loading the view.
}
}
extension ProfileVC: UICollectionViewDelegateFlowLayout {
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//this is just a test number, here you should return the number of your items
return 20
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//you should set reuseIndetifier in storyboard or in code if you create your collectionView programmatically
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "reuseIdentifier", for: indexPath)
//again this is just a placeholder color to see that it's working
cell.backgroundColor = .green
return cell
}
func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, sizeForItemAt: IndexPath) -> CGSize {
let screenWidth = collectionView.bounds.width
//be sure to use floor or to use a number of items that perfectly fits the width of collectionView
return CGSize(width: floor(screenWidth/3), height: floor(screenWidth/3))
}
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 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
}
I want to add that if the width of your collectionView is not perfectly divisible by 3, you will always have some gap between the cells. You should do the math by yourself for each device width and choose a proper number of cells for the width. For example for a screen with aspect ratio like iPhone X, I would use 3 cells when in portrait and 4 cells when in landscape.
You can do like this:
func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, sizeForItemAt: IndexPath) -> CGSize {
let screenWidth = collectionView.bounds.width
let screenHeight = collectionView.bounds.height
let numberOfCells : CGFloat = screenWidth > screenHeight ? 4 : 3
return CGSize(width: floor(screenWidth/numberOfCells), height: floor(screenWidth/numberOfCells))
}
I have a collection view where two cells are in a row. Both have the same size and I want to have the same distance between them no matter how many cells are in the collection view.
Now I have found out that the distance only does not work with the iPhone 11 Pro. Be it on a real device or in a simulator.
Here are two images facing this problem:
This is my code for the size and spacing:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if storeModel.count != 0 {
let bounds = collectionView.bounds
let heightVal = self.view.frame.height
let widthVal = self.view.frame.width
let cellsize = (heightVal < widthVal) ? bounds.height/2 : bounds.width/2
return CGSize(width: cellsize - 10 , height: cellsize - 10 )
} else {
let width = collectionView.frame.width
let height = collectionView.frame.height
return CGSize(width: width - 20, height: height)
}
}
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 10
}
I set sections for my horizontal UICollectionView. I want my cell item in second rows in each section to be double width of items in first row. This is what i set:
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 0 {
return 2
}
return 1
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { //1.7, 2.7
if indexPath.row == 0 {
return CGSize(width: collectionView.frame.size.width/2,
height: collectionView.frame.size.height/2)
}
return CGSize(width: collectionView.frame.size.width,
height: collectionView.frame.size.height/2)
}
But this is the end result:
How i can get rid of spaces in smaller size items and layout them nicely?
Note: Numbers on pictures represent section and row (0 1 -> Section 0, Row 1)
This is what i want to achieve:
First: if you use a storyboard, make sure you add collection view's constraints properly to its super view.
Second: Add collection view cell's constraints.
Third: From storyboard's size inspector, change collection view's estimate size to none.
Fourth: Add UICollectionViewDelegateFlowLayout in your viewcontroller
Fifth: Add those following code:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.section == 0 {
return CGSize(width: (collectionView.frame.size.width - 10)/2,
height: (collectionView.frame.size.height - 10)/2) // your desired height
}
return CGSize(width: (collectionView.frame.size.width - 10),
height: (collectionView.frame.size.height - 10)/2)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0.0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
Replace indexPath.row with indexPath.section.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { //1.7, 2.7
if indexPath.section == 0 {
return CGSize(width: collectionView.frame.size.width/2,
height: collectionView.frame.size.height/2)
}
return CGSize(width: collectionView.frame.size.width,
height: collectionView.frame.size.height/2)
}
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 with cells of equal size. I want to make the distances between cells and between the cells and the left and right of the collection view all equal, with 2 cells in each row.
The content of each cell is horizontally centered, so I've tried setting the width of the cells to half the collection view's width:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: eventCollectionView.frame.width/2, height: 111)
}
But this produces the following:
As seen, the two cells are at 1/4 and 3/4th of the width. Instead I want them to be at 1/3 and 2/3 so the space between them and the edges are all equal.
Does anybody know how I'd accomplish this?
use bellow code
fileprivate var defaultspacing: CGFloat = 5
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
var size: CGSize = .zero
let width = (SCREEN_WIDTH - (3 * defaultspacing))/2
size = CGSize(width: width, height: width)
return size;
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(self.defaultspacing, self.defaultspacing, 0, self.defaultspacing);
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return self.defaultspacing;
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat
{
return self.defaultspacing;
}
For that, you have to set section Inset property from storyboard whatever you want from left and right side of the collection view, also set the Min Spacing property from storyboard you want
In my case, I want 5 px from both the side and 5 px between 2 cell.
and in delegate method write,
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (eventCollectionView.frame.width - 15)/2, height: 111)
}
Add following method in your class
You can make changes as per your needs
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: CGFloat(collectionView.frame.size.width / 2 -10), height: CGFloat(111)) // here 10 is the space of cells
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAt section: Int) -> UIEdgeInsets{
return UIEdgeInsetsMake(2, 2, 2, 2)
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 10.0
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10.0
}
Pictures make a lot of sense. My solution will work in every devices in right format.
Why?
Please go through the solution.
Step 1:
My base layout is iphone 7 plus and i have calculated for this devices.Calculation is done because i need to support all devices.So i had make some constants like below:
var SCREEN_WIDTH = UIScreen.main.bounds.size.width
var SCREEN_HEIGHT = UIScreen.main.bounds.size.height
var BASE_SCREEN_HEIGHT:CGFloat = 736.0
var SCREEN_MAX_LENGTH = max(SCREEN_WIDTH, SCREEN_HEIGHT)
var ASPECT_RATIO_RESPECT_OF_7P = SCREEN_MAX_LENGTH / BASE_SCREEN_HEIGHT
let MINIMUM_INTERITEM_SPACING:CGFloat = 46 //My Default Inter Cell Spacing for iphone 7plus as i have designed in it.
var ITEM_WIDTH:CGFloat = 138 //for iPhone 7 plus.initial value
var ITEM_HEIGHT:CGFloat = 138 //for iphone 7 plus.initial value
let NUMBER_OF_CELLS_IN_PORTRAIT:CGFloat = 2
let NUMBER_OF_CELLS_IN_LANDSCAPE:CGFloat = 5
Step 2:
Then i had to calculate cellsize according the exact space between cells.
func calculateCellSize(screenWidth:CGFloat , cellItem:CGFloat) {
ITEM_WIDTH = (screenWidth - MINIMUM_INTERITEM_SPACING * 3 *
ASPECT_RATIO_RESPECT_OF_7P * (cellItem-1)) / cellItem - 1// This 1
has been subtracted from ITEM_WIDTH to remove mantissa
ITEM_HEIGHT = ITEM_WIDTH
}
Step 3:
For between cell spacing you need to call below method.
public func collectionView(_ collectionView: UICollectionView, layout
collectionViewLayout: UICollectionViewLayout,
minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return MINIMUM_INTERITEM_SPACING * ASPECT_RATIO_RESPECT_OF_7P
}
And for Padding(with left margin and right margin) you have to call this method.
public func collectionView(_ collectionView: UICollectionView, layout
collectionViewLayout: UICollectionViewLayout, insetForSectionAt
section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(0,
MINIMUM_INTERITEM_SPACINGASPECT_RATIO_RESPECT_OF_7P, 0,
MINIMUM_INTERITEM_SPACINGASPECT_RATIO_RESPECT_OF_7P)
}
Overall i had to call 4 methods.I didn't explain the other two methods may be you will understand.
So basically whole code is like this.
And my output is