CollectionView sections - swift

I have a collectionview and I want it to be of n section where each section has a ten of cells, my problem is: Maybe n equals thirty five, in this case I want to show 3 section with ten of cells and the last section with just five.

If array count is 35 return count/10 if count%10 is 0 else return count/10+1 in numberOfSections method
In numberOfItemsInSection method multiply current section with 10 and subtract from count. return min value of 10 or subtracted value
In cellForItemAt method multiply section with 10 and add row to get the array index
class ViewController: UIViewController, UICollectionViewDataSource {
var arr = Array(1...35)
func numberOfSections(in collectionView: UICollectionView) -> Int {
return (arr.count/10) + (arr.count%10 == 0 ? 0 : 1)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return min(10,arr.count - (10*section))
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as? Cell
let currentStr = arr[(indexPath.section*10)+indexPath.item]
cell?.label.text = "\(currentStr)"
return cell!
}
}

You can simply implement UICollectionViewDataSource methods and configure collectionView(_:numberOfItemsInSection:) method based on the each section for number of cells.
let n = 35 //It specify the total elements count
func numberOfSections(in collectionView: UICollectionView) -> Int {
return n/10
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch section {
case (n/10):
return (n % 10)
default:
return 10
}
}
In the above code, the collectionView will have
(n % 10) cells for last section
10 cells for other sections
Kindly clarify your conditions so I can update the code accordingly.

You can split the array into chunks using this extension
extension Array {
func chunked(into size: Int) -> [[Element]] {
return stride(from: 0, to: count, by: size).map {
Array(self[$0 ..< Swift.min($0 + size, count)])
}
}
}
If count is 35 -> [10,10,10,5]
If count is 30 -> [10,10,10]
If count is 29 -> [10,10,9]
Then use the two dimensional array in collectionview delegate methods
class ViewController: UIViewController, UICollectionViewDataSource {
let array = Array(1...35)
lazy var chunkedArray = array.chunked(into: 10)
func numberOfSections(in collectionView: UICollectionView) -> Int {
return chunkedArray.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return chunkedArray[section].count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
print(chunkedArray[indexPath.section][indexPath.item])
return cell
}
}

Related

How to remove filtered items from collection view in swift?

I am developing an application in swift where you have the possibility to filter the data displayed in the UICollectionView. After the data is filtered, collection view should update, so only items which fit the filter will be visible(eg. price is more than 30$). I can't update the collection view, I've tried literally everything...(reloadData(), deleteItems(at: IndexPath), batch, etc...) Here is the code for collection view functions:
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dogsSort.count
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.bounds.width, height: 150)
}
internal func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedDog = dogsSort[indexPath.row]
print("selected dog " + selectedDog.name)
let vc = self.storyboard!.instantiateViewController(withIdentifier: "ItemVC") as! ItemViewController
vc.dog = selectedDog
self.navigationController!.pushViewController(vc, animated: true)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.imageView.layer.cornerRadius = 10
/*storageWizard.getImage(path: "/dogs/" + dogsSort[indexPath.row].id + "/image.png", imageCompletionHandler: { (image) -> Void in
cell.imageView.image = image
self.dogsSort[indexPath.row].image = image
return
})*/
print(indexPath.row)
if dogsSort.count > indexPath.row {
cell.btnPrice.setTitle("Get for " + String.init(self.dogsSort[indexPath.row].price) + "$", for: .normal)
cell.labelView.text = self.dogsSort[indexPath.row].name
cell.CityView.text = self.dogsSort[indexPath.row].description
cell.breedView.text = self.dogsSort[indexPath.row].breed
cell.ageView.text = self.dogsSort[indexPath.row].age
cell.weightView.text = self.dogsSort[indexPath.row].weight
}
return cell
}
And code which I am using currently, dummy code just to test the functionality, but it is not working...
dogsSort.remove(at: 0)
collectionView.deleteItems(at: [IndexPath(row: 0, section: 0)])
collectionView.reloadData()
If I understand you question this is what you have to do:
get your main array of dogs;
filter it:
filtered = dogsArray.filter({ $0.price > 30 })
If you want to filter the items based on a certain property, set again:
filtered = dogsArray.filter({$0.name == "something"})
In your CollectionView's delegate and data source methods you need to work with the filtered array, ie. (for the number of items in row):
filtered.count

Collection view loading only one cell at a time

Edited
I am using custom collection cells to load in collectionview. But when the collection view loadeds expecting behaviour cellforindex delegate will call for 3 indexpath but in this case only calling 0th indexpath 3 time.
override func viewDidLoad()
{
super.viewDidLoad()
let nib = UINib(nibName: "cellName", bundle: nil)
self.collectionvView?.register(nib, forCellWithReuseIdentifier: "cellId")
}
func numberOfSections(in collectionView: UICollectionView) -> Int
{
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return 10
}
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
print("indexpath.row ----------------- ",indexPath.row)
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId",for: indexPath) as! DetailCollectionViewCell
return cell
}
Check All This
self.arrIds.count = 3
numberOfSections = 1
numberOfItemsInSection
cellForItemAt

How to add a button/default UICollectionViewCell at IndexPath 0 without getting "Fatal error: Index out of range"

I'm trying to add a default cell to use as a "Add New" button in my cellForItemAt method whilst still returning the correct number of items in my collection view, however, the methods I have tried either return an incorrect number of items or cause a crash Index out of range
I'm trying to achieve this.
This is my numberOfItemsInSection method.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == newsCollectionView {
if newsArticles.count > 0 {
return newsArticles.count + 1
} else if newsArticles.count == 0 {
return 1
}
}
return 0
}
This is my cellForItemAt method
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == newsCollectionView {
if indexPath.row == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "newsDefaultCell", for: indexPath) as UICollectionViewCell
return cell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "newsArticleCell", for: indexPath) as! newsArticleCell
cell.news = newsArticles[indexPath.row + 1]
return cell
}
}
return UICollectionViewCell()
}
I know that this line is causing the crash
return newsArticles.count + 1
I'm just looking for an alternative way of doing it without returning 1 less item than I should, any help is hugely appreciated.
You're adding when you should subtract.
Replace this line:
cell.news = newsArticles[indexPath.row + 1]
with:
cell.news = newsArticles[indexPath.row - 1]
Because when the indexPath.row is 1, you want the first item from your array (ie. the item at index 0).
Also, you can simply your numberOfItemsInSection by observing that if newsArticles.count is 0, then newsArticles.count + 1 will be 1, so there's no reason to special case it.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView === newsCollectionView {
return newsArticles.count + 1
}
return 0
}
Note: Use === instead of == to check that the two items are references to the same object.

1 section and 2 cells but 4 indexPath.row

Swift 4.
I want to return two cells in one section, so in my class CollectionViewController I do :
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1 }
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2 }
I see two cells but if I print indexPath.row in the code below (still in the same class), I see 0 1 0 1. Why is it not only one 0 1as I have only two cells in one section ?
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! CollectionViewCell
print(indexPath.row)
return cell
}
cellForItemAt is getting called four times. Your numberOfItemsInSection is hard-coded for 2. When the view loads cellForItemAt is called twice (once for each cell). It is then getting called twice again when you call reloadData() from your DispatchQueue.main.async closure.
Updated - How to avoid the first call:
You need to store your cell data in an array, and only populate the array just before you call reloadData(). Thus, the array will be empty when the view is first loaded.
var yourArray = [YourObject]() //Empty Array
//..
DispatchQueue.main.async {
//append your two items to the yourArray
yourArray.append(/*cell1data*/)
yourArray.append(/*cell2data*/)
self.collectionView?.reloadData()
}
//..
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return yourArray.count
}

Why does my UICollectionViewController not show anything?

I'm trying to make a UICollectionViewController show a grid of cells (in which I'll later add text, likely as textViews if possible). I have:
class GridViewController : UICollectionViewController{
//(NOW GONE) #IBOutlet weak var gridViewTable = UICollectionViewController()
let arr1 : [String] = []
let arr2 : [String] = []
override func viewDidLoad(){
super.viewDidLoad()
self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "gridCell")
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 4
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath as IndexPath)
cell.backgroundColor = UIColor.red
return cell
}
}
I used the interface builder to connect white where the cells will go to the gridViewTable. Nothing shows up at all, even tho it runs and compiles. How can I get the cells to show?
please use these code:
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 4
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath as IndexPath)
cell.backgroundColor = UIColor.red
return cell
}
You don't need the outlet for a UICollectionViewController, the class itself takes care of that.
Also, make sure that the view controller's class in Interface Builder is set to GridViewController in the Identity Inspector (3rd tab from the left in the right-most pane).