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.
Related
When I want to see an array in a collection view in swift, it gets multiplied by the amount of sections in the collection view. Why is this and how do I stop it?
let numbers = ["one", "two", "three"]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3;
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let theCell = collectionView.dequeueReusableCell(withReuseIdentifier: "theCell", for: indexPath) as! cell
for number in numbers {
print("\(number)")
}
return theCell
}
I hoped that it would return:
one
two
three
but instead it returned:
one
two
three
one
two
three
one
two
three
Since you have three cells per section, and assuming you have only one section, collectionView(_:cellForItemAt:) gets called three times. Each time you are printing all the elements of the numbers array. To only print the element corresponding to the current index, replace the for loop by:
print(numbers[indexPath.row])
How can I have a UICollection View, with One cell in the first row, and three cells in the second row?
I want to display a picture in the first row, and below it 3 pictures.
You need to use collectionView's Datasource method number of section to specify how many section you required in your collectionview. in that section you can pass number of cell you want to display. and in cell for item you can set data according to section and Item value.
try like this.
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3 // you can return as per your requirement
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 0 {
return 2 // number of cell you want to display in each section
}else if section == 1 {
return 3 // number of cell you want to display in each section
}else {
return 4 // number of cell you want to display in each section
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifierCollection, for: indexPath) as! HomeCollectionViewCell
return cell
}
You need to implement your own custom layout. By default the collection view has UICollectionViewFlowLayout, that fills the cells in a horizontal/vertical grid.
You can change this behaviour with your own layout, i.e. a class implementing UICollectionViewLayout.
To get started, I would recommend checking this and this tutorials.
I have 2 arrays
var list3:Array< String > = Array < String >()
var list4:Array< String > = Array < String >()
I need to return the count of list 3 if the tableview Section is 0
and
I need to return the count of list 4 if the tableview Section is 4
How can i do this ?
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch(section){
case 0 : return list3.count
case 1 : return list4.count
default : return 0
}
}
it is not working...
In the overrided func of the UITableViewController when you create the UITableViewCell you will get from the indexPath the section and the row and you will set up your collection view inside the cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = ...
if(indexPath.row == 3 && indexPath.section == 1){
cell.numberOfCells = 123
}
}
Reading your question and some of the comments and your answers, I understand is :
you have a table view; in that table view one of the tableview cell (TVC) has a collection view. And based on whether this TVC is in section 0 or 1, you need yo decide number of elements that collection view.
You can achieve this by:
Setting tag value of collection view to appropriate section number
when you are creating the TVC.
Then based on this tag, you can
decide whether to populate the collection view using list3 or list4.
If my understanding on your problem and suggested solution looks fine
and you need code snippet, do let me know and I will add it. Thanks.
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
}
I have a collection view of 20 items stored in pictures array. I am deleting selected items with this code:
self.collectionView.performBatchUpdates({
let selectedItems = self.collectionView.indexPathsForSelectedItems()!
self.picturesObject.deleteItemsAtIndexPaths(selectedItems)
self.collectionView.deleteItemsAtIndexPaths(selectedItems)
}, completion: {_ in})
this is the implementation of picturesObject's deleteItemsAtIndexPaths:
func deleteItemsAtIndexPaths(indexPaths:[NSIndexPath]){
for indexPath in indexPaths{
pictures.removeAtIndex(indexPath.item)
}
}
The problem I'm facing is the following. Every time a selected item in collection view is deleted, the pictures array gets smaller. If I delete the first item, than pictures array is only 19 items big. If I than try to delete item number 20 in collection view, the array index gets out of bounds because pictures array is now only 19 object big, but I wanted to delete the 20iest object stored in indexpath.item.
What is the correct way of deleting items in collection view?
EDIT:/// collection view methods
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
collectionView.allowsMultipleSelection = true
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return picturesObject.pictures.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! ShopCell
cell.imageView.image = picturesObject.pictures[indexPath.item]
return cell
}
Before looping through your array in deleteItemsAtIndexPaths, sort your indexPaths in descending order. Then, if you delete item number 19, the next to delete is guaranteed to be less than 19.