Swift CollectionView (fatal error: Index out of range) - ios

I am implementing a UICollectionView inside each cell of a UITableView
Now I am getting data from an API and keeping this data in an Array, and the problem is that the UICollectionView delegate method numberOfItemsInSection is of course being called before even the array is getting filled with data so I am getting Index out of range error.
The question is how to give maybe a default value to the count being returned or how to mutate the array.
Here is the code i am using:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// let cell = collectionView.superview as! MainTableViewCell
// let index = tableView.indexPath(for: cell)
let countIndex = self.consultations[collectionView.tag].images.count
return countIndex
}

You need to check array found and it's count.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let consultationsTmp = self.consultations, let colTag = consultationsTmp[collectionView.tag],let imagesTmp = colTag.images
{
return imagesTmp.count
}
return 0
}

I solved the issue with a different method.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let cell = collectionView.superview?.superview?.superview as! MainTableViewCell
print("SSSSSSSSSSSSSSSSS\(cell)")
let index = self.tableView.indexPathForRow(at: cell.center)
print("EEEEEEEEEEEEEE\(index?.row)")
let countIndex = self.consultations[index?.row ?? 00].images.count
return countIndex
}

Related

Swift CollectionView first cell add user button and other cell load from image array

In my case, I am trying to create a UICollcetionView with first cell add button for add user and from second cell I need to load Image array string into another cells. Here, I am using two separate cell but I am not getting first cell into my output. Please provide me solution or another best method.
I am trying to get below output
Multiple Cell Code
// MARK: CollectionView Delegate
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.section == 0 {
let firstCell = collectionView.dequeueReusableCell(withReuseIdentifier: "addusercell", for: indexPath) as! AddParticipantsCollectionViewCell
return firstCell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "userscollectionview", for: indexPath as IndexPath) as! ParticipantsCollectionViewCell
cell.imageView?.image = self.imageArray[indexPath.row]
return cell
}
}
You need
if indexPath.item == 0 {
// first cell
}
else {
// second cell
cell.imageView?.image = self.imageArray[indexPath.item - 1]
}
Correct dataSource method ( remove it )
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1 // Section should set return 1 or default is optional
}
And change numberOfItemsInSection to
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArray.count + 1
}
If you have two sections in the collectionView you also need to set the number of items in each individual section:
// MARK: CollectionView Delegate
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 0 {
return 1 // return 1 item in cell (+ cell)
} else {
return imageArray.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.section == 0 {
let firstCell = collectionView.dequeueReusableCell(withReuseIdentifier: "addusercell", for: indexPath) as! AddParticipantsCollectionViewCell
return firstCell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "userscollectionview", for: indexPath as IndexPath) as! ParticipantsCollectionViewCell
cell.imageView?.image = self.imageArray[indexPath.row]
return cell
}
}
I hope it's been of any help.

numberOfItemsInSection for multiple UICollectionView inside UIViewController Swift

I have three UICollectionView inside one UIViewController. Then I set IBOutlet for each UICollectionView
#IBOutlet weak var firstCollectionView: UICollectionView!
#IBOutlet weak var secondCollectionView: UICollectionView!
#IBOutlet weak var thirdCollectionView: UICollectionView!
and I set delegate and datasource for them. But when I want to set numberOfItemsInSection like below
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == firstCollectionView {
return 1
} else if collectionView == secondCollectionView {
return 2
} else if collectionView == thirdCollectionView {
return 3
}
}
It keeps giving me this error Missing return in a function expected to return 'Int'
You need to return 0 because in numberOfItemsInSection you must have to return Int. you ended with else if but numberOfItemsInSection still need return Int value.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == firstCollectionView {
return 1
} else if collectionView == secondCollectionView {
return 2
} else if collectionView == thirdCollectionView {
return 3
} else {
return 0
}
}
For better readability & a little less code, I suggest you use a switch statement:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch collectionView {
case firstCollectionView:
return 1
case secondCollectionView:
return 2
case thirdCollectionView:
return 3
default:
return 0
}
}
Personally, I'd make a separate dataSource class for each collectionView, & assign them in viewDidLoad -
firstCollectionView.dataSource = FirstDataSource()
secondCollectionView.dataSource = SecondDataSource()
thirdCollectionView.dataSource = ThirdDataSource()
& either give the required data to each dataSource when it's created or make the dataSource have responsibility for getting the data as well. Putting everything into the viewController in a situation like this leads to madness...
Simple example -
class MyDataSource: UICollectionViewDataSource {
var data: [MyData] = [] // set this when instantiating, or provide the means of creating the data which you would have put in the viewController
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell: MyCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellIdentifier", for: indexPath) as? MyCollectionViewCell else {
return UICollectionViewCell()
}
// do stuff to cell
}
}
In viewController -
self.firstCollectionView.dataSource = MyDataSource()
// etc...
Very straight forward, all it's doing is separating the code into areas of responsibility, makes it a whole lot easier to manage

UIcollectionview display different cell after certain row

I need to create a UIcollectionview with grids of cells with 3 cells per row. After a certain amount of cells, lets say 60, I need to display a big cell with image view cell 61(different set of data). But with creating this 61st cell of image, my normal data on 61st will be merged into this image cell, is there any way to calculate it without compromising my array of data . Thanks.
Here's the data source part:
var items = [Item]()
func numberOfSections(in collectionView: UICollectionView) -> Int {
let numberOfSections = 60/(items.count+1) + 1
return numberOfSections
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let numberOfSections = 60/(items.count+1) + 1
if section != numberOfSections - 1 {
return 60
}
let numberOfItems = items.count%60
return numberOfItems
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let itemIndex = indexPath.section * 60 + indexPath.row
let item = items[itemIndex]
// set cell from item …
}
Then you need to provide the header cell using UICollectionViewDelegate:
func collectionView(_ collectionView: UICollectionView, willDisplaySupplementaryView view: UICollectionReusableView, forElementKind elementKind: String, at indexPath: IndexPath)
Search SO for 'willDisplaySupplementaryView' will show you how to implement this. Good luck!

How to hide the cell in tableview if json data nil

How to hide the cell collectionview in section if json data is nil. im loading collectionview in a tableview cell.my code is belowim showing array of images in collection view i have used third party for horizontalautomatic scroll as third party for collection view
func setCollectionData(_collectionData: [Any]) {
self.collectionData = _collectionData as NSArray
collectionViewObj.setContentOffset(CGPoint(x:0,y:0), animated: true)
for i in 0 ..< collectionData.count {
self.strings = StringClass()
self.strings.vehicleImage = (collectionData[i] as AnyObject).value(forKey: "img_name") as? String
self.strings.vehicleTitle=(collectionData[i] as AnyObject).value(forKey: "p_name") as? String
self.modelArray.append(self.strings)
}
collectionViewObj.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
if self.collectionData.count >= 4 {
return 4
}
else
{
return collectionData.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ProductCollectionViewCustomCell", for: indexPath)as! ProductCollectionViewCustomCell
strings = modelArray[indexPath.row]
cell.ProductTitleLabel.text = strings.vehicleTitle
let URLBaseString = "http://vehiclebuzzzz.com/"
let url = URL(string:URLBaseString .appending(strings.vehicleImage!))
let dataurl = try? Data(contentsOf: url!)
cell.productImageViewObj.image=UIImage(data: dataurl!)
return cell
}
func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout: UICollectionViewLayout,sizeForItemAt indexPath: IndexPath) -> CGSize
override this method and return zero size CGSize.zero if the json data is nil else return actual size of cell
OR
remove nil data from array and call collectionView.reloadData()
Update the collectionData array with the new data & then perform collectionViewObj.reloadData()
-If you does not want show collection view. just hide the collection view until you get the data.
-When you data make it visible

The indexPath of a specific UITableViewCell is always nil

After the update to Xcode8 the following code doesn't work anymore
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let cell = collectionView.superview!.superview as! UITableViewCell
let table = cell.superview!.superview as! UITableView
let indexPath = table.indexPath(for: cell)
Now the value of indexPath is always nil.
Any ideas?
I've solved the problem by replacing the last line with
let indexPath = table.indexPathForRow(at: cell.center)

Resources