Collectionview Second item starts from centre instead of Top - ios

I am trying to copy the search layout of Instagram for the collectionview. I have tried doing it but my second item starts from the center instead of top which is causing the problem.
Actual Output:
Expected Outlput:
Here is my code
extension UIViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 12
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CVCell", for: indexPath) as! CVCell
cell.backgroundColor = .lightGray
cell.lbl.text = "\(indexPath.item)"
cell.lbl.textAlignment = .center
cell.layer.borderColor = UIColor.black.cgColor
cell.layer.borderWidth = 0.5
return cell
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.item == 0{
return CGSize(width: (UIScreen.main.bounds.width / 3) * 2, height: (UIScreen.main.bounds.width / 3) * 2)
}
return CGSize(width: UIScreen.main.bounds.width / 3, height: UIScreen.main.bounds.width / 3)
}

Related

expanding UICollectionViewCell on tap

I have UICollectionView that scrolls up vertically, and i want to change item height in tap. In default state, cell have a height of 2/3 screen height. I tried following:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
if let cell = cell as? TutorialCell {
let attribute = layout.layoutAttributesForItem(at: indexPath)
attribute?.frame = CGRect.init(x: 0, y: 0, width: UIScreen.main.bounds.size.width,
height: UIScreen.main.bounds.size.height)
collectionView.reloadItems(at: [indexPath])
}
print(indexPath.row)
}
But it's not working.
You can Achieve this by UICollectionViewDelegateFlowLayout method sizeForItemAt
Declare a variable
var selectIndex: Int = -1
In your didSelectItemAt add this
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.selectIndex = indexPath.row
collectionView.reloadData()
}
Add an extension for UICollectionViewDelegateFlowLayout
extension YourViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let height = (self.selectIndex == indexPath.row ) ?
UIScreen.main.bounds.size.height :
(UIScreen.main.bounds.size.height * 2) / 3
return CGSize(width:UIScreen.main.bounds.size.width, height:height)
}
}

How to set custom collectionView Cell Frame?

I want first UICollectionViewCell by full width of screen and further UICOllectionViewCell is 2*2.
For that i have tried below code.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
let cell : FirstCell = collectionView.dequeueReusableCell(withReuseIdentifier: "FirstCell", for: indexPath) as! FirstCell
cell.layoutSubviews()
return cell
}
else {
let cell : SecondCell = collectionView.dequeueReusableCell(withReuseIdentifier: "SecondCell", for: indexPath) as! SecondCell
cell.layoutSubviews()
return cell
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.item == 0 {
let width = (UIScreen.main.bounds.size.width - 16)
return CGSize(width: width, height: 200)
}
else {
let width = (UIScreen.main.bounds.size.width - 24) / 2
let height = (UIScreen.main.bounds.size.width * 200) / 320
return CGSize(width: width, height: height)
}
}
But the Output comes as below.
Requirement: First Green Cell is of full Width and 2nd Yellow cell comes not from Center but it comes from left side and 3rd, 4th and so on cell comes 2*2
Any help will be appreciated.
Thanks in Advance.
To achieve this, you should take 3 section in collection view.
section 1 - 1 cell
section 2 - 1 cell (width should be less than half of available width for collection cell)
section 3 - number of cell you wanted
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return section == 2 ? 4 : 1
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
switch indexPath.section {
case 0:
return CGSize(width: UIScreen.main.bounds.width - 20, height: 200)
case 1:
return CGSize(width: (UIScreen.main.bounds.width - 30) / 2, height: 200)
default:
return CGSize(width: (UIScreen.main.bounds.width - 30) / 2, height: 200)
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectioncell", for: indexPath)
cell.backgroundColor = .red
return cell
}
}
Your view will look like:

ios - How to merge two different sections in collection view

I want to merge two different sections that uses two different cells respectively(cellA and cellB). Using the following code I could get a collection view with two sections.
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource{
#IBOutlet var testCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
self.testCollectionView.delegate = self
self.testCollectionView.dataSource = self
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 0 {
return 3
} else {
return 1
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.section == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "taskCell", for: indexPath) as! CellA
cell.layer.cornerRadius = 2
cell.layer.shadowColor = UIColor(red:0.82, green:0.82, blue:0.82, alpha:1.0).cgColor
cell.layer.shadowOffset = CGSize.zero
cell.layer.shadowOpacity = 1
cell.layer.shadowRadius = 4
cell.layer.masksToBounds = false
return cell
} else {
let cell2 = collectionView.dequeueReusableCell(withReuseIdentifier: "addNewCell", for: indexPath) as! CellB
return cell2
}
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = testCollectionView.bounds.width/2 - 30
let height = width
return CGSize(width: width, height: height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(30, 20, 10, 20)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 20
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(indexPath.row)
}
}
And the output looks like this.
Now, I want the section 1(CellB) to be merged with section 0(CellA). Like this.
I couldn't find the solution to achieve this. Any help will be appreciated.
You can show both cell types in the same section with these changes:
Add properties for the number of aCells and bCells. This is better than putting the magic numbers 3 and 1 in your code because it documents what the numbers represent. If you ever need to change them, you can change them in one spot.
Change numberOfSections to return 1.
Change numberOfItemsInSection to return the sum of the number of A cells and the number of B cells: return aCells + bCells.
In cellForItemAt, compare indexPath.item to aCells to figure out when to switch over to B cells.
var aCells = 3
var bCells = 1
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return aCells + bCells
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item < aCells {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "taskCell", for: indexPath) as! CellA
cell.layer.cornerRadius = 2
cell.layer.shadowColor = UIColor(red:0.82, green:0.82, blue:0.82, alpha:1.0).cgColor
cell.layer.shadowOffset = CGSize.zero
cell.layer.shadowOpacity = 1
cell.layer.shadowRadius = 4
cell.layer.masksToBounds = false
return cell
} else {
let cell2 = collectionView.dequeueReusableCell(withReuseIdentifier: "addNewCell", for: indexPath) as! CellB
return cell2
}
}

Center the last row in UICollectionView

I have a 7 cells UICollectionView displayed like this
X X
X X
X X
X
I need it to be displayed like this
X X
X X
X X
X
How's this can be done in Swift?
you can do this simple calculation in your collectionViewLayout function.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
//return itemSize
let spaceBetweenCell :CGFloat = 0
let screenWidth = UIScreen.main.bounds.size.width - CGFloat(2 * spaceBetweenCell)
let totalSpace = spaceBetweenCell * 1.0
if indexPath.row == categories.count-1 {
// check if last cell is odd
if categories.count % 2 == 1 {
return CGSize(width: screenWidth , height: (screenWidth-totalSpace)/4) // all Width and same previous height
}else {
return itemSize
}
}else{
return itemSize
}
}
Here is my code just i add datasource count to 7 items
Also make sure that you create your cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier:
"cell", for: indexPath) as! UICollectionViewCell
, Spacing between items =4
Code
extension ViewController :UICollectionViewDelegate , UICollectionViewDelegateFlowLayout,UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 7 // data source
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! UICollectionViewCell
cell.backgroundColor = .red
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let spaceBetweenCell :CGFloat = 4.0
let screenWidth = UIScreen.main.bounds.size.width - CGFloat(2 * spaceBetweenCell)
let totalSpace = spaceBetweenCell * 1.0; // there is one space in 2 item
if indexPath.row == 6 { // indexpath.row == datasource.count - 1
if 6 % 2 == 1{ // check if last cell is odd
return CGSize(width: screenWidth , height: (screenWidth-totalSpace)/2) // all Width and same previous height
}else{
return CGSize(width: (screenWidth - totalSpace)/2, height: (screenWidth-totalSpace)/2)
}
}else{
return CGSize(width: (screenWidth - totalSpace)/2, height: (screenWidth-totalSpace)/2)
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 4.0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 4.0
}
}

UICollectionView with multiple rows

I use UICollectionView (views with UIButtons) to book the time:
override func viewDidLoad() {
super.viewDidLoad()
collectionViewTime.register(UINib(nibName: "TimeCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "timecell")
collectionViewTime.dataSource = self
collectionViewTime.delegate = self
if let layout = collectionViewTime.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .vertical
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Timeline.count
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
let numberOfItems = CGFloat(collectionView.numberOfItems(inSection: 0))
let combinedItemWidth = (numberOfItems * flowLayout.itemSize.width) + ((numberOfItems - 1) * flowLayout.minimumInteritemSpacing)
let padding = (collectionView.frame.width - combinedItemWidth) / 2
return UIEdgeInsets(top: 0, left: padding, bottom: 0, right: padding)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "timecell", for: indexPath) as! TimeCollectionViewCell
let time = Timeline[indexPath.row][0]
cell.btnTime.setTitle(DateManager.dateToTime(date: time.0), for: .normal)
return cell
}
And I got all views (UIButtons) in one line:
How can I set automatically moving UIViews in new rows?
So, in my case (iPad) one row will contain 7 UIViews, iPhone will contain 4-5 UIViews in one row.
For that you need to implement UICollectionViewDelegateFlowLayout method collectionView(_:layout:sizeForItemAt:) and change the collectionView's Scroll direction to Vertical
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let minCellSpace: CGFloat = 10 //Set minimum cell space that you want
if UIDevice.current.userInterfaceIdiom == .pad {
//For iPad return 7 cell as you mention in question
return CGSize(width: (collectionView.frame.size.width / 7) - minCellSpace, height: yourCellHeight)
}
else {
//For iPhone return 4 cell, if you want to return 5 cell divide width to 5
return CGSize(width: (collectionView.frame.size.width / 4) - minCellSpace, height: yourCellHeight)
}
}
Please use one more flow delegate method as below
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize;
------
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width:(button width), height:button hieght);
}

Resources