Setting a UILabel inside a UICollectionViewCell - ios

So the thing is I have 4 UICollectionViewCells inside CollectionView that is inside a TableView. (I set the TableViewController as the DataSource and Delegate of the CollectionView).
Now, I've stored a string array of 4 elements inside a CKRecord. How can I set the label inside the 4 cells, so that they display each string of the array?
Here's what it looks like:
func collectionView(collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! CvCell
let poll = polls[indexPath.row] // polls is a [CKRecord]()
let labelContent = poll["4strings"] as? [String]
cell.cellLabel.titleLabel?.text = labelContent
return cell
}
Now obviously, that's not going to work since I basically set the label of each cell to the array itself. How can I write a for loop that goes through each CvCell label, or rather how can I specify the label of e.g. the third cell to set it to the value of labelContent[2]?
UPDATE:
Totally forgot to mention, the data structure in the cloud basically looks like this:
array1 = [1a, 1b, 1c, 1d]
array2 = [2a, 3b, 2c, 2d]
array3 = [3a, 2b, 3c, 3d]
array4 = [4a, 4b, 4c, 4d]
And if I try to do what #user3353890 proposed, it's giving me the following results for my tableview:
tableview1cell - collectionview1 : [1a, 2b, 3c, 4d] -> these are the collectionviewcell labels
tableview2cell - collectionview2 : [1a, 2b, 3c, 4d]
tableview3cell - collectionview3 : [1a, 2b, 3c, 4d]
tableview4cell - collectionview4 : [1a, 2b, 3c, 4d]
However what I want is:
tableview1cell - collectionview1 : [1a, 1b, 1c, 1d]
tableview2cell - collectionview2 : [2a, 2b, 2c, 2d]
tableview3cell - collectionview3 : [3a, 3b, 3c, 3d]
tableview4cell - collectionview4 : [4a, 4b, 4c, 4d]
I'm sorry I have a really hard time explaining this, but I hope someone gets what I'm trying to do?

First, you're not returning one cell. You're going to ultimately return 4 cells because you stipulated that you want 4 items in each section in the numberOfItemsInSection method.
func collectionView(collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return 4
}
cellForItemAtIndexPath coordinates how you want to display each cell. Because you stated that there are 4 items in a section, this method will get called 4 times, returning 1 cell each time it is called to be an item in that section.
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! CvCell
let poll = polls[collectionView.tag] // polls is a [CKRecord]()
let labelContent = poll["4strings"] as? [String]
// This should give you the string that you want.
let myString = labelContent[indexPath.row]
// Display the string in the label.
cell.cellLabel.titleLabel?.text = myString
return cell
}
After you set the labelContent array, get myString at each index by passing indexPath.row into the array. So for the 1st cell (0 index) it gives you the 1st string in your array (0 index).
Edit
when you create poll, use indexPath.section in order to keep the correct order of all your arrays while displaying data.
let poll = polls[collectionView.tag]
Edit 2
In your tableView cellForRowAtIndexPath Method, when you create a cell, set the cell's collectionView.tag to indexPath.section
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("customCell", forIndexPath: indexPath) as! CustomCell
cell.collectionView.delegate = self
cell.collectionView.dataSource = self
cell.collectionView.tag = indexPath.section
return cell
}
Then when you dequeue your collectionView, you can access the proper array by calling collectionView.tag -as seen above- in order to get the indexPath of the tableViewCell.

A better solution so you don't have to nest a collectionView within a tableView:
This gives you the number of sections you want. One section for each array in your "polls" array:
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return polls.count
}
This will take the sub-array for each corresponding section and allow the number of items in that collectionView section to correspond with the number of items in the array:
func collectionView(collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
let poll = polls[section] as? [String]
return poll.count
}
Display your collectionView cells here:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! CvCell
let poll = polls[indexPath.section] // polls is a [CKRecord]()
let labelContent = poll["4strings"] as? [String]
// This should give you the string that you want.
let myString = labelContent[indexPath.row]
cell.cellLabel.titleLabel?.text = myString
return cell
}

Related

Getting indexPath of tableView inside tableViewCell file during cell creation/dequeueing

I am creating a scrollable view filled with monthly calendars. I'm using a collection view to display a calendar inside a table view full of calendars. So each table view cell is a calendar for a specific month, and each collection view cell is a day. I have a separate swift file for the tableview cell from the view controller. Since each tableview cell is going to look different (because different months), the tableview cell needs to know which row it is placed in inside the tableview during its creation in dequeque cell function.
tableView.dequeueReusableCell(withIdentifier: "CalendarTableViewCell", for: indexPath)
I need to get the indexPath in the "for: indexPath" parameter inside the tableview cell file because the collectionview inside the tableview cell gets created when the tableview cell is dequeued. The contents of the collection view depends on which tableview row it's in. So how do I get that parameter?
Sorry for the long explanation, please help if possible. Thank you!
Create an array in UITableViewCell subclass and use that array in collection view data source methods.
class MonthCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
let datesArray = [String]()
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return datesArray.count
}
}
In tableView cellForRowAt method assign the date values and reload the collectionView
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell") as! MonthCell
cell.datesArray = []//dates based on indexPath
cell.collectionView.reloadData()
return cell
}
Or
Assign a reference to the table view cell index path in tableView cellForRowAt method
class MonthCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
var tableIndexPath:IndexPath?
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let tableIndexPath {
// return value based on tableIndexPath
} else {
return 0
}
}
}
//cellForRowAt
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MonthCell") as! MonthCell
cell.tableIndexPath = indexPath
cell.collectionView.reloadData()
return cell
}

Refresh collectionView on viewdidload after retrieving UserDefaults

I have a collection view, and you can select the items in it and toggle them on and off by changing the background colour. The cells are toggled on/off thanks to a boolean I have in an arrow I made for all of the cells. I have saved the bool value but when I try to write them back into the array and use collectionView.reloadData()the app crashes. My collectionViewcode is:
extension OLLViewController: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { //set the amount of items in the CollectionView to the amount of items in the OLLData dictionary
return OLLData.OLLCasesList.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { //set each cell to a different mamber of the dict.
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "OLLCell", for: indexPath) as! OLLCell
cell.imageView.backgroundColor = OLLData.OLLCasesList[indexPath.item]._isSelected ? UIColor.orange : UIColor.clear //change colour if selected
let image = OLLData.OLLCasesList[indexPath.item]._imageName
cell.label.text = image
cell.imageView.image = UIImage(named: image)
let savedIsSelected = defaults.bool(forKey: Key.isSelected)
OLLData.OLLCasesList[indexPath.item]._isSelected = savedIsSelected
//collectionView.reloadData() //when uncommented it crashes the app
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { //detect if case selected and reload CollectionView
let caseName = OLLData.OLLCasesList[indexPath.item]._imageName
print(caseName, OLLData.OLLCasesList[indexPath.item]._isSelected)
OLLData.OLLCasesList[indexPath.item]._isSelected = !OLLData.OLLCasesList[indexPath.item]._isSelected
defaults.set(OLLData.OLLCasesList[indexPath.item]._isSelected, forKey: Key.isSelected)
collectionView.reloadItems(at:[indexPath])
collectionView.reloadData()
if OLLData.OLLCasesList[indexPath.item]._isSelected == true { //if the item is selected, add to selectedCases array
selectedCases.append(OLLData.OLLCasesList[indexPath.item]._id)
selectedCaseNames.append(OLLData.OLLCasesList[indexPath.item]._imageName)
print(selectedCases, selectedCaseNames) //debugging
numberOfSelectedCases.text = String(selectedCases.count)
}
else if OLLData.OLLCasesList[indexPath.item]._isSelected == false { //remove from selectedCases array
selectedCases.removeAll(where: { $0 == OLLData.OLLCasesList[indexPath.item]._id })
selectedCaseNames.removeAll(where: { $0 == OLLData.OLLCasesList[indexPath.item]._imageName })
print(selectedCases, selectedCaseNames) //debugging
numberOfSelectedCases.text = String(selectedCases.count)
}
}
._isSelectedis the boolean that says whether the cell is 'toggled'.
Any ideas would be greatly appreciated.
First of all, uncommenting that line will produce an infinite loop. cellForRowAt happens because the collection view is reloading, so calling a refresh while the collection view is refreshing is no good.
So your issue is that you don't know how to display selected cells in your collection view, right?
Here's a function that fires right before the collection view is about to display a cell:
func collectionView(_ collectionView: UICollectionView,
willDisplay cell: UICollectionViewCell,
forItemAt indexPath: IndexPath)
{
<#code#>
}
Inside this function, you should:
Cast cell into your OLLCell (safely if you want to be thorough)
Look at your data and see if the cell should be selected OLLData.OLLCasesList[indexPath.item]._isSelected
Ask your casted cell to change its colors/UI/appearance according to your ._isSelected boolean
Step 3 has a VERY important caveat. You should be changing the UI when ._isSelected is false AND when it's true. Because the collection view reuses cells, old UI state will randomly recur. So setting it every time is a good way to ensure the behavior you want.
Here's an example:
func collectionView(_ collectionView: UICollectionView,
willDisplay cell: UICollectionViewCell,
forItemAt indexPath: IndexPath)
{
//Cast the vanilla cell into your custom cell so you have access
//to OLLCell's specific functions and properties.
//Also make sure the indexPath falls in the indices of your data
if let myCastedCell = cell as? OLLCell,
0 ..< OLLData.OLLCasesList.count ~= indexPath.item
{
myCastedCell.imageView.backgroundColor = OLLData
.OLLCasesList[indexPath.item]._isSelected
? UIColor.orange
: UIColor.clear
}
}

Updating collection view items

I have inserted a collectionView inside my tableViewCell. Tableview contains the list of categories and the collectionView contains all the product. How can I have a different number of items in the collectionView based off of which table view row was selected? I've tried storing the selected table view row and using that to define the number of items to be returned however it either crashes with no error code, tells me the value is nil or just does not display any clitems in the collectionView. Any help would be greatly appreciated. Thank you for your time.
Below is my code:
My Custom table view cell:
extension ExpandableCell: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let toReturn = categoryItems.count
return counter
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCell", for: indexPath) as! CustomCollectionViewCell
//What is this CustomCollectionCell? Create a CustomCell with SubClass of UICollectionViewCell
//Load images w.r.t IndexPath
print(self.selectedCategory.description)
let newArray = starbucksMenu[selectedCategory]
//cell.image.image = UIImage(named: (allItems[selectedCategory]?[indexPath.row])!)
cell.label.text = categoryItems[indexPath.row]
//cell.layer.borderWidth = 0.1
return cell
}
My table view delegate method:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.row)
word = indexPath.row
guard let cell = tableView.cellForRow(at: indexPath) as? ExpandableCell
else { return }
switch cell.isExpanded
{
case true:
self.expandedRows.remove(indexPath.row)
self.selectedCategory = ""
case false:
self.expandedRows.insert(indexPath.row)
}
self.selectedCategory = categories[indexPath.row]
print(self.selectedCategory)
//self.array = starbucksMenu[starbucksMenuCategories[indexPath.row]]!
//self.collectionView.reloadData()
cell.menuItems = allItems[selectedCategory]!
cell.categoryItems = allItems[selectedCategory]!
cell.isExpanded = !cell.isExpanded
self.itemsArray = allItems[selectedCategory]!
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
I've tried many things, I've tried adding the items in an array and returning the count (displays nothing). I have a dictionary with the necessary items so I've also tried returning allItems[selectedCategory]?.count and this always returns an error, I believe selectedCategory has no value once this is called.
Make a for loop of collection view item with appropriate operation Between beginUpdate() and endUpdate()

Updating Collection View Number of Items based on selected table view row

Basically, in my project I have a collection view within a table view cell, it all works fine apart from trying to detect the number of items needed for the collection view which will vary based on what table view row they selected.
The collection view works fine if I set a fixed valued for the numberOfItemsInSection, but that value would only work for 1 table view cell. I've stored the string value of the selected table view row and try to pass it to my custom table view cell which contains the collection view so it can update but it always returns nil.
Any ideas on how to get it to automatically updated based on the selected table view row?
Below is my code:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return categoryItems.count // THIS IS WHERE I GET AN ERROR
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCell", for: indexPath) as! CustomCollectionViewCell
//What is this CustomCollectionCell? Create a CustomCell with SubClass of UICollectionViewCell
//Load images w.r.t IndexPath
print(self.selectedCategory.description)
let newArray = starbucksMenu[selectedCategory]
//cell.image.image = UIImage(named: (allItems[selectedCategory]?[indexPath.row])!)
cell.label.text = categoryItems[indexPath.row]
//cell.layer.borderWidth = 0.1
return cell
That is my Collection View Code.
Below is the Table View Delegate function that sets the values:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.row)
word = indexPath.row
guard let cell = tableView.cellForRow(at: indexPath) as? ExpandableCell
else { return }
switch cell.isExpanded
{
case true:
self.expandedRows.remove(indexPath.row)
case false:
self.expandedRows.insert(indexPath.row)
}
self.selectedCategory = categories[indexPath.row]
print(self.selectedCategory)
cell.selectedCategory = selectedCategory
//self.array = starbucksMenu[starbucksMenuCategories[indexPath.row]]!
//self.collectionView.reloadData()
cell.menuItems = allItems[selectedCategory]!
cell.categoryItems = allItems[selectedCategory]!
cell.isExpanded = !cell.isExpanded
self.itemsArray = allItems[selectedCategory]!
self.tableView.beginUpdates()
self.tableView.endUpdates()
}

indexPath.row always returns zero

I have two collection view and one displays names and the other one displays age of the corresponding person. This data is stored inside array of dictionary in a form of "[["Name","Age"],["Name": "Daniel", "Age" : "20"],["Name":"Jake","Age":"20"]]. This data comes from CSV file, so the first element is a header. Inside collectionView cellForItemAtIndexPath, I'm checking collection view and provide data base on row number like cell[indexPath.row]["Name"] and cell2[indexPath.row]["Age"]. However, indexPath.row always returns zero, so I'm getting just headers -
How do fix this issue? This is my code -
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
if collectionView == self.nameCollectionView {
let nameCell = collectionView.dequeueReusableCellWithReuseIdentifier("NameCell", forIndexPath: indexPath) as! NameCell
nameCell.data.text = self.data?[indexPath.row]["Name"]
println(indexPath.row)
return nameCell
}
else{
let ageCell = collectionView.dequeueReusableCellWithReuseIdentifier("AgeCell", forIndexPath: indexPath) as! AgeCell
ageCell.data.text = self.data?[indexPath.row]["Age"]
return ageCell
}
}
As par your code you are setting numberOfItemsInSection only 1 then you always get 0th index. make there is dynamic value for example return Array.count.
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.data.count // here you need to set dynamic count of array
}
UPDATE:
If you followed numberOfSectionsInCollectionView then make your code like following of cellForRow:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
if collectionView == self.nameCollectionView {
let nameCell = collectionView.dequeueReusableCellWithReuseIdentifier("NameCell", forIndexPath: indexPath) as! NameCell
nameCell.data.text = self.data?[indexPath.section]["Name"]
println(indexPath.section)
return nameCell
}
else{
let ageCell = collectionView.dequeueReusableCellWithReuseIdentifier("AgeCell", forIndexPath: indexPath) as! AgeCell
ageCell.data.text = self.data?[indexPath.section]["Age"]
return ageCell
}
}
IndexPath is a property which has the following structure
Indexpath {Section, Row}.
So if you want your data in two different section with a single row in them then indexpath.row for each of them is going to return 0 as because
For section index 0 - Indexpath[0,0] meaning indexpath of section index 0 and row index 0
For section index 1 - Indexpath[1,0] meaning indexpath of section index 1 and row index 0
Hope could make you understand.
As others have pointed out, you are telling your collection views that you always have 2 sections and 1 item in each section. Thus the collection view will only ever ask for 1 item in each section. Thus there will only ever BE one item in each section (index 0).
You say "This data is stored inside dictionary in a form..."
Is it a dictionary or an array of dictionaries? A dictionary is an unordered collection, so it is not appropriate for storing an ordered set of items for feeding to a collection view or table view. An array of dictionaries is appropriate. From the data you show, and your code, it looks like you have an array of dictionaries. You should edit your question to make that clear.
Your code doesn't really make sense. You have 2 different collection views, and you have each display different data. You tell your collection views that you have 2 sections but ignore the section number and create the same data for both sections. Something is wrong there.

Resources