Index out of range : UICollectionView inside a UITableViewCell - swift

I'm trying to put a UICollectionView in a UITableViewCell, but I guess I'm doing wrong...
So here is my code :
PanierMenuCell is UITableViewCell
extension PanierMenuCell : UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return listProduit.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cellIdentifier = "PanierCollectionCell"
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as? PanierCollectionCell else {
fatalError("erreur de conversion !!")
}
cell.ONom.text = listProduit[indexPath.row].nomProduct
cell.OQtt.text = "x\(listProduit[indexPath.row].nbProduct)"
cell.OImage.image = UIImage(named: "test")
cell.backgroundColor = .gray
cell.layer.borderWidth = 1
cell.layer.borderColor = UIColor.lightGray.cgColor
cell.layer.cornerRadius = 5
return cell
}
}
Here is my class calling the UITableViewCell :
extension PanierVC : UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listPanierProduct.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "PanierMenuCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? PanierMenuCell else {
fatalError("erreur de conversion !!")
}
let produit = listPanierProduct[indexPath.row].product as! Menu
let listProduit = produit.getListProducts()
cell.listProduit = listProduit
cell.OTotal.text = "Total: \(produit.prix)0€"
cell.ONomMenu.text = "Menu : \(produit.nom)"
cell.backgroundColor = .gray
return cell
}
So here is the problem :
When I'm using several cell with collectionView inside without scrolling the tableview, that's working well, the problem is when I need to scroll down the tableView (the tableview is so reloading the cell), that's causing an error : "Thread 1: Fatal error: Index out of range" in
cell.ONom.text = listProduit[indexPath.row].nomProduct
The item insides the UICollectionView are king of mixed together, I mean that a tableviewcell will take some items which are suppose to be in other tableviewcell, like if the cell.listproduit was mixed... Don't know how clear I was, but I can explain more if needed.
Any ideas on how I should solve this ?
Thank's a lot

You need to reload your UICollectionView after changing the data source. This will update the number of items in the collection view.
So after this line:
cell.listProduit = listProduit
Add this line (Assuming you have a property referencing your UICollectionView):
cell.collectionView.reloadData()

Related

How to get all collectionview cell index inside tableview cell using Protocol in Swift

I am using collectionview inside tableview cell. so when collectionview cell button is clicked then present viewcontroller i am using protocol..
code for tableviewcell and delegate:
protocol CustomCellDelegate: class {
func sharePressed(cell: ProposalTableVIewCell)
}
class ProposalTableVIewCell: UITableViewCell, UICollectionViewDelegate,UICollectionViewDataSource {
#IBOutlet weak var attetchmentsCollectionview: UICollectionView!
var delegate: CustomCellDelegate?
public var bidAttatchment: Array<Get_attachments>?
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return bidAttatchment?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AttatchmentCollectionViewCell", for: indexPath) as! AttatchmentCollectionViewCell
let attatchBid = bidAttatchment?[indexPath.item]
cell.attatchmentLbl.text = attatchBid?.filename
cell.openBtn.tag = indexPath.item
cell.openBtn.addTarget(self, action: #selector(connected(sender:)), for: .touchUpInside)
return cell
}
#objc func connected(sender: UIButton){
delegate?.sharePressed(cell: self)
}
code for viewcontroller: when i press sharePressed getting only collectionview's first cell value.. how to get all cells value.. please do let me know
class ViewMyAppliedReqVC: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate, CustomCellDelegate{
func sharePressed(cell: ProposalTableVIewCell) {
guard let index = tableView.indexPath(for: cell)?.row else { return }
let name = getBitDetails?.result?.bid?.get_attachments?[index].filename// always getting only first cell value
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ViewProposalTableVIewCell1", for: indexPath) as! ViewProposalTableVIewCell1
cell.bidAttatchment = getBitDetails?.result?.bid?.get_attachments
cell.delegate = self
cell.attetchmentsCollectionview.reloadData()
return cell
}
You are always getting same index because you are taking out index of "ProposalTableVIewCell" and this is tableView cell. And collectionView cells are in the same tableView cell.
Solution:
Take another parameter in protocol function like below for storing index of collection cell
protocol CustomCellDelegate: class {
func sharePressed(cell: ProposalTableVIewCell,collectionCellIndex:Int)
}
func sharePressed(cell: ProposalTableVIewCell, collectionCellIndex:Int) {
guard let index = tableView.indexPath(for: cell)?.row else { return }
let name = getBitDetails?.result?.bid?.get_attachments?[collectionCellIndex].filename// This will return index of collection cell
}
#objc func connected(sender: UIButton){
delegate?.sharePressed(cell: self,collectionCellIndex: sender.tag )
}

set image to imageview in uicollectionview that is in uitableviewcell xib

I have taken a UITableView and deque a UITableViewCell XIB and set data to a label in it, also I have UICollectionView in UITableViewCell XIB in which I have to set images in a UICollectionViewCell.
UIImageView is in UICollectionViewCell and respective UICollectionView is in UITableViewCell so how to set an image to that UIImageView?
also have tableview delegate function in one file
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.discoverData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "discoveryTableViewCell", for: indexPath) as! discoveryTableViewCell
let data = discoverData[indexPath.row]
cell.messageLbl.text = (data["content"] as! String)
// This how normally image is set in tableview, how to do with collectionview
cell.userImage.pin_updateWithProgress = true
let riderImage = URL(string: (data["profile_pic"] as! String))
cell.userImage.pin_setImage(from: riderImage, placeholderImage: nil, completion: nil)
return cell
}
collection view delegate function in UITableViewCell.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return numCell
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "discoverCell", for: indexPath as IndexPath) as! discoverCollectionViewCell
cell.postImage.backgroundColor = .cyan
return cell
}
Here is a picture of how it looks yellow box is UICollectionView and cyan color box is UIImageView.
In cellForRowAt indexPath:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "discoveryTableViewCell", for: indexPath) as! discoveryTableViewCell
if let data = discoverData[indexPath.row] as? [String: Any] {
cell.messageLbl.text = (data["content"] as! String)
cell.data = data
}
return cell
}
In your "discoveryTableViewCell" (It should be "DiscoveryTableViewCell") :
var data: [String: Any] {
didSet {
collectionView.reloadData()
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "discoverCell", for: indexPath as IndexPath) as! discoverCollectionViewCell
if let profilePic = data["profile_pic"] as? String {
let riderImage = URL(string: profilePic)
cell.postImage.pin_setImage(from: riderImage, placeholderImage: nil, completion: nil)
}
return cell
}
You should follow the proper style guide and naming conventions in Swift.
Please check this URL for better understanding:
https://github.com/raywenderlich/swift-style-guide

UICollectionView inside UITableViewCell how pass data to Uicollectionviewcell

I have a tableview controller with a collectionview inside it , in the tableviewcell i implemented a collection view with horizontal scrolling, the data appears not correct
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = friendsCollectionView.dequeueReusableCell(withReuseIdentifier: "friends", for: indexPath) as! friendCollectioncell
friendsCollectionView.delegate = self
friendsCollectionView.dataSource = self
let ava = Myfriends[indexPath.item]["ava"] as! String
let firstname = Myfriends[indexPath.row]["firstName"] as! String
let lastname = Myfriends[indexPath.row]["lastName"] as! String
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Myfriends.count
}
problem is in
return Myfriends.count
always empty so not thing appears but if a change to 1 or 2. it works ok
in uitableviewcontroller:
var myFriends: [Dictionary<String, Any>] = []
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// section 1 which includes 1 cell that shows my friends
if indexPath.section == 0 {
// accessing cell in main.storyboard that has id "friends". This cell stores my friends
let cell = tableView.dequeueReusableCell(withIdentifier: "friends", for: indexPath) as! friends
cell.Myfriends = myFriends
}
return cell
// section 2 (or any other sections) that shows all the posts of the user
} else {
return cell
}
Thank You in advance
Edit:
If I understand this right:
problem is in return Myfriends.count always empty so not thing appears but if a change to 1 or 2. it works ok
probably your collectionView(_:numberOfItemsInSection:) is called before you this cell.Myfriends = myFriends is set.
In your friends tableViewCell:
class friends: UITableViewCell .. {
...
var Myfriends: [Friend] {
didSet {
friendsCollectionView.reloadData()
}
}
}
Imho your collectionView(_:numberOfItemsInSection:) isn't called at all.
This code
friendsCollectionView.delegate = self
friendsCollectionView.dataSource = self
in your collectionView(_:cellForItemAt:) is useless, because if this method is called, dataSource (and probably delegate) are already set correctly.
Instead, change your tableView(_:cellForRowAt:) to
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView...
cell.friendsCollectionView.delegate = cell
cell.friendsCollectionView.dataSource = cell
return cell
} else {
...
}
}
or set friendsCollectionView dataSource and delegate in your friends cell, where you init your collectionView.
If that shouldn't help you, check where and how you populate your myFriends dictionary.
Besides that, I'd recommend that you create a struct Friend:
struct Friend {
let ava: String
let firstName: String
let lastName: String
}
and change your var myFriends to
var myFriends: [Friend] = []
That would make it easier and safer to access the values for your friendsCollectionCell:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = friendsCollectionView...
let friend = MyFriends[indexPath.item]
let ava = friend.ava
...
return cell
}

Display different tableview data depending on which collectionview cell is selected

Here's a screenshot of the UI
I have a collectionview above my tableview. I'm trying to get the tableview's data to change based on which collectionview cell is selected.
This view handles menu item selection, and menu items are displayed in the tableview. The categories for these menu items are displayed in a horizontally-scrolling collectionview right above the tableview.
The restaurant class has an instance variable .categories which returns an array of strings. This is what populates the collectionview. The menu item class has an instance variable .category which returns a single string. I intend to match these two.
My intended result: For each selected cell in the collectionview, take its category name and communicate it to the tableview. The tableview should take this name and filter through its menu items to check for matching category names. Display those specific tableview cells.
For my collectionview:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = restaurant?.categories.count {
return count
} else {
return 0
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! CategoryCell
// Sets label text and images for each category
cell.categoryLabel.text = restaurant?.categories[indexPath.row]
if UIImage(named: (restaurant?.categories[indexPath.row])!) != nil {
cell.buttonView.image = UIImage(named: (restaurant?.categories[indexPath.row])!)
}
return cell
}
For my tableview:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let count = restaurant?.menuItems.count {
return count
} else {
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = masterTableView.dequeueReusableCell(withIdentifier: "menuCell", for: indexPath) as! MenuItemCell
cell.selectionStyle = UITableViewCell.SelectionStyle.none
let currentItem = restaurant?.menuItems[indexPath.row]
cell.item = currentItem
return cell
}
Right now, my result (obviously) is that simply all of my menu items are being displayed in the tableview, regardless of which collectionview cell is selected.
Two things needed in following format for your question.
1. restaurant?.categories values:
[popular, milk tea, snacks, tea, ...]
2. restaurant?.menuItems values:
{
popular : [
{
"name" : "thai-tea",
"price": "$6",
"thumbnailURL" : "https://.../thai.png"
},
{
"name" : "milk-tea",
"price": "$3",
"thumbnailURL" : "https://.../ml.png"
}
],
snacks : [
{
"name" : "brownie",
"price": "$7",
"thumbnailURL" : "https://.../brw.png"
},
{
"name" : "pasta",
"price": "$3",
"thumbnailURL" : "https://.../pas.png"
}
]
}
Global Variable
var whichCellSelect : Int = 0
CollectionView
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
whichCellSelect = indexPath.item
yourTableView.reloadData()
}
Tableview
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let count = restaurant?.menuItems.count {
return count
} else {
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = masterTableView.dequeueReusableCell(withIdentifier: "menuCell", for: indexPath) as! MenuItemCell
cell.selectionStyle = UITableViewCell.SelectionStyle.none
let currentCategory = restaurant?.categories[whichCellSelect] // You will get String here
let currentMenuItem = restaurant?.menuItems[currentCategory] // You will get Array here
cell.item = currentItem
return cell
}
Notes
Make sure, restaurant?.categories data type as [String] and restaurant?.menuItems data type as [String : Any] . This is one of the simplest way to understand data transfer within UIViewController
How are the different categories stored? Are you using an array for each category and sending that specific array to the TableView after an option in the CollectionView is selected? Then you would need to refresh the TableView so the changes are reflected in the interface.
I alsso can't see where you're getting what the user pressed in the CollectionView. ... can you not use something like this ...
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
to get what they've selected, use that choice to update the table by writing a different array or only adding certain array elements depending on the category then use reloadData() to refresh the table.

Create model data from firestore and show them by id with different UICollectionView

I have three different collections in Firestore.
I'm appending those data in those models after fetching them from Firestore with these codes.
My UI is a multiple UICollectionView inside multiple UITableView. I want to create collectionView with packages count and feed them with JsonStickerPack models.
This is my code but this one didnt worked because i couldnt correctly feed those JsonStickerImageEmoji model. I have to seperate them with something :)
let sp = JsonStickerImageEmoji(imageFile: Stickerdata!, emojis: (packageModel?.description)!)
let sP1 = JsonStickerPack(stickers: [sp], trayImageFile: trayData!,
identifier: docPack.documentID ,
name: (packageModel?.name)!,
licenseAgreementWebsite: (appX?.licenseAgreementWebsite)!,
publisher: (appX?.publisher)!,
privacyPolicyWebsite: (appX?.privacyPolicyWebsite)!,
publisherWebsite: (appX?.publisherWebsite)!)
let sP2 = JsonSticker(stickerPacks: [sP1], iosAppStoreLink: (appX?.iosAppStoreLink)!,
androidPlayStoreLink: (appX?.androidPlayStoreLink)!)
Collectionview codes :
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellReuseId, for: indexPath) as? CustomCollectionViewCell
print("\(cell?.cellImageView.image)")
cell?.cellImageView.image = UIImage(data : trayImages[indexPath.row])
return cell!
}
My viewcontroller codes :
func numberOfSections(in tableView: UITableView) -> Int {
return categories.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 160
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell") as? CustomTableViewCell
if cell == nil {
cell = CustomTableViewCell.customCell
}
let aCategory = self.categories[indexPath.section]
cell?.updateCellWith(category: aCategory)
cell?.trayImages = trayImages
cell?.stickerImage = stickerImage
cell?.stickerInfoArray = stickerInfoArray
cell?.stickerPackages = stickerPackages
cell?.stickerModel = stickerModel
cell?.cellDelegate = self
return cell!
}
This is the result of these codes.
I have to seperate them. Cat to cat etc..