correct way of deleting items in collection view - ios

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.

Related

Why is array in collection view getting multiplied by amount of sections?

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 to properly move array item when `UICollectionViewCell` position is changed

I have a UICollectionView, on which user can change position of the cells, after looking at every Swift Drag and Drop page. I settled on a method to be able to update the array of strings the keep track of the collection view in this function:
func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let positionPreviouslyHoldingTheSelectedIndex = list[sourceIndexPath.item]
// put the select index in the intended destination index
list[sourceIndexPath.item] = list[destinationIndexPath.item]
// put the cell/index of the destination index in the selected positions old index
list[destinationIndexPath.item] = positionPreviouslyHoldingTheSelectedIndex
}
But above code update the array as expected and it gets weird, how I would update the array/list once the moveItemAt is called.
There are 2 IndexPaths, you have to get rid of the array element from sourceIndexPath and add it to destinationIndexPath, as such:
let item = list.remove(at: sourceIndexPath.item)
list.insert(item, at: destinationIndexPath.item)

How to manage two different cells in a collection view

This is a really basic wireframe of what I'm trying to achieve:
This is what I currently have in Xcode, resulting in 3 cells per row, with no space in between. The date label cell isn't showing up because I don't know how to get cellForItem to recognize it along with the post cells (hence the question):
I have two separate cell classes - one for the date labels and one for the rows of images.
My collection view methods are as follows:
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return posts.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "postCell", for: indexPath) as! PostCell
let dateCell = collectionView.dequeueReusableCell(withReuseIdentifier: "dateLabelCell", for: indexPath) as! DateLabelCell
cell.postImage.loadImageUsingCacheWithUrlString(posts[indexPath.row].pathToImage)
cell.postID = posts[indexPath.row].postID
cell.postImage.contentMode = UIViewContentMode.scaleAspectFill
// Here I'll have to get the date and have the labels display the days of the week properly
dateCell.dateLabel.text = "Monday"
return cell
}
But I'm not sure how to manage both cells in the above methods. I'll have to sort the posts by a timestamp so things posted on a certain day get added to the correct day's row, but that's something I'll do another day - for now I'm just wondering how I can get the UI laid out so that the collection view shows a date label, then a row of post cells, and repeat.
Thanks for any advice!

How to keep only distinct elements in Array while selecting and deselecting cells?

I have a collection view with different cells, and I want to be able to select multiple cells. I am able to do so, but when I click a cell that has already been selected, that same cell is again added to the array resulting in a repeat. What I want to happen is when it is clicked it will append the label to the array, and when it is clicked again it will delete it from the array. Below is what I have so far.
func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
arrayOfFriendsSelected.append(arrayOfFriendsNames[indexPath.item])
print(arrayOfFriendsSelected)
var cell = collection.cellForItemAtIndexPath(indexPath) as! ShareCell
cell.friendimage.layer.borderWidth = 3.0
cell.friendimage.layer.borderColor = UIColorFromRGB("4F26D8").CGColor
return true
}
func collectionView(collectionView: UICollectionView, shouldDeselectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
var cell = collection.cellForItemAtIndexPath(indexPath) as! ShareCell
arrayOfFriendsSelected.removeAtIndex(indexPath.item)
print(arrayOfFriendsSelected)
cell.friendimage.layer.borderWidth = 0.0
return true
}
Maintain a Set instead of Array.
Declare like
var setOfSelectedFriends = Set<String>()
and add
setOfSelectedFriends.insert(arrayOfFriendsNames[indexPath.item])
For removing, use
setOfSelectedFriends.remove(<elementToRemove>)
You can read more about Swift Sets here.

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