How to create 2 x 2 grid layout using Collection View? - ios

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)
}

Related

Bottom Collection view spacing issues, I want to remove the blue spacing

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))
}

UICollectionview has unwanted space between sections

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)
}

Swift: Change the width of cell Layout in UICollectionView when add items

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
}

How to show three columns in a CollectionView using swift4 in all devices

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if collectionView == self.collectionViewVideo {
var collectionViewSize = collectionViewVideo.frame.size
collectionViewSize.width = collectionViewSize.width/3.0 //Display Three elements in a row.
return collectionViewSize
} else {
return CGSize(width: 60, height: 60)
}
}
You also need to take space in account.
Space is your collection view item space
Following is the code has two cells and 20 spaces.
(I am not writing exactly code you need You should do it by your self)
extension YourViewController : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 20
}
//--------------------------------------------------------------------------------
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 20;
}
//--------------------------------------------------------------------------------
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: ( self.collectionView.frame.size.width - 60 ) / 2,height:( self.collectionView.frame.size.width - 60 ) / 2)
}
//--------------------------------------------------------------------------------
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(0, 20, 20, 20)
}
}
Here 60 means SPACE 20 CELL SPACE 20 CELL SPACE 20

i want to show two cell in one row in uicollectionview in all iphone model

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.

Resources