UICollectionView - insertItems(at: indexPath) not working - ios

I have an array with some elements in it which my UICollectionView displays. I then go and fetch more elements and append it to my array. I want to then tell the UICollectionView that elements have been added to the datasource and to update the UI.
I tried this but it is not working:
// Add more data to my existing array
myArray.append(contentsOf: moreElements)
let indexPath = [IndexPath(row: myArray.count-1, section: 0)]
myCollectionView.insertItems(at: indexPath)
I get this error but I am not sure if I am doing something wrong.
EDIT: I do NOT want to use myCollectionView.reloadData()

Your issue is that you need an IndexPath for each item you are adding to the collection view. You add multiple objects to myArray but then you only pass one IndexPath to insertItems. And this is the cause of the error.
Try the following:
var paths = [IndexPath]()
for item in 0..<moreElements.count {
let indexPath = IndexPath(row: item + myArray.count, section: 0)
paths.append(indexPath)
}
myArray.append(contentsOf: moreElements)
myCollectionView.insertItems(at: paths)

Related

Swift How can i take data from custom uicollectionviewcell?

I have a form page like below image. I should take value from this form. I have a custom collection view from the bottom. I should take textfield value from the cell. how can I do that?
I use the picker view in those cells. Can you show a little example for me?
Question: "I should take textfield value from the cell. how can I do that?"
Answer: You shouldn't do that.
View objects are for displaying information to the user and for collecting input, not for storing data.
This is especially important for UICollectionViews and UITableViews, since both of those create and recycle cells as needed as the user scrolls.
You should design your collection view to have a data model object (typically an array of structs, or an array of arrays for data in rows and columns).
As the user enters data into your text fields and taps return, you should collect the changed values and save them to your data model. Then your data model is always up-to-date and if you need to fetch the value for a specific IndexPath you just look up the data in the model.
If you know the indexPath for your cell, its easy like:
let indexPath = IndexPath(item: index, section: 0)
let cell = collectionView.cellForItem(at: indexPath) as? GuestDataCell
let value = cell?.txtDataValue.text
EDIT after comments:
Not best practice, but if you need:
var i = 0
var numberOfCells = 4
var values: [String]!
while i < numberOfCells {
let indexPath = IndexPath(item: index, section: 0)
let cell = collectionView.cellForItem(at: indexPath) as? GuestDataCell
let value = cell?.txtDataValue.text
values.append(value)
i += 1
}
But I dont really recommend you to write like this. Read please Duncan's answer.

Issue using Tags to retrieve data in TableView

When a user presses a button within a tableView cell I need to get that cell's data from the array the tableView was populated with. After using that data for something that cell is then removed from the tableView.
I'm using Tags to know the index of the cell who's button was pressed. However, once a cell in the middle of the tableView is removed along with its data from the array, the cells further down in the tableView now have buttons with tags that are out of the array's bound. For example:
array.length = 3
Cell 0 (Button Tag: 0)
Cell 1 (Button Tag: 1)
Cell 2 (Button Tag: 2)
User then presses button inside Cell 1, which is then removed from the array and the table:
Cell 0 (Button Tag: 0)
Cell 2 (Button Tag: 2)
The problem now is that if the user presses Cell 2, when I try get that cell's data from the array using the Tag the app will crash since I'm accessing an out of bounds index. Therefore I'm starting to think that using tags isn't a good option and I'm wondering what's a better altenative. Here is my code:
var invitesArray = [userInfo]()
func declineInvite(sender: UIButton!) {
let info = invitesArray[sender.tag]
// I use info to process data
// Remove cell's data from array and remove cell from table
inviteTable.beginUpdates()
inviteTable.deleteRows(at: [IndexPath(row: sender.tag, section: 0)], with: UITableViewRowAnimation.automatic)
invitesArray.remove(at: sender.tag)
inviteTable.endUpdates()
}
// Populating the tableView
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = inviteTable.dequeueReusableCell(withIdentifier: "inviteCell", for: indexPath) as! joinCell
cell.declineButton.tag = indexPath.row
cell.declineButton.addTarget(self, action: #selector(self.declineInvite), for: UIControlEvents.touchUpInside)
return cell
}
Can anybody help correct my naive ways please?
Do not use tags for this. UITableView has a function indexPathForCell that will give you the indexPath. Since your model should be in synch with your tableView when you add or remove items, you should use this indexPath to index into the array. Tags are rarely necessary and usually bad practice.

How to get indexpath from row index Swift

I am loading an array to UIcollectionview(later will add other data) . I want to select collection view item randomly as:
var indexpath = Int(arc4random_uniform(UInt32(items.count)-1)
self.collectionview.selectitem(at: **indexpath**, animated:true ,scrollPosition:UICollectionViewScrollPosition)
Here it’s giving me error at bold position saying:
cannot convert value of type Int to expected argument type 'IndexPath?
I can understand this exception as indexpath is different in swift from collection data index(array index). But I am unable to find any method that can convert item index to indexpath or anyother way.
I am newer to iOS so might be not good at searching correct keywords for such issue. It will be a great favour from you all for any other method to this requirement.
To create an IndexPath in swift 3 for UICollectionView
let indexPath = IndexPath(item: 0, section: 0)
You can use it
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
Swift 3 Latest
self.table.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .fade)
Swift 4.1. Here I created function to get NSIndexPath. Just pass your UIView(UIButton,UITextField etc) and UICollectionView object to get NSIndexPath.
func getIndexPathFor(view: UIView, collectionView: UICollectionView) -> NSIndexPath? {
let point = collectionView.convert(view.bounds.origin, from: view)
let indexPath = collectionView.indexPathForItem(at: point)
return indexPath as NSIndexPath?
}

Programmatically altering NSIndexPath in Swift

I'm implementing AdMob in a UITableView by putting banner ad in the first row of a section. I'm most of the way there implementing it, however I'm having a tough time getting cellForRowAtIndexPath to work as desired.
This is what my numberOfRowsInSection looks like:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count = Int()
if let sections = fetchedResultsController.sections {
let currentSection = sections[section]
count = currentSection.numberOfObjects
count = count + 1 // add another row for an ad
}
return count
}
My cellForRowAtIndexPath looks like this:
override func tableView(tableView: UITableView, var cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let adCell: BannerAdTableViewCell = tableView.dequeueReusableCellWithIdentifier(BannerAdTableViewCell.reuseIdentifier(), forIndexPath: indexPath) as! BannerAdTableViewCell
// customization
return adCell
} else {
// Cell for vanilla item to display
// TODO: fix indexpath here. need to add 1
let newIndexPath = indexPath.indexPathByAddingIndex(indexPath.row+1)
indexPath = newIndexPath
// Cell for a Routine
let customCell = tableView.dequeueReusableCellWithIdentifier(RoutineSelectionTableViewCell.reuseIdentifier(), forIndexPath: indexPath) as! RoutineSelectionTableViewCell
let routine = fetchedResultsController.objectAtIndexPath(indexPath) as! SavedRoutines
customCell.routineNameLabel.text = routine.routineTitle
return customCell
}
}
I know I need to adjust the value of the indexPath to account for an extra row in the indexPathSection, but everything I've tried triggers out of bounds exceptions of some sort. Any feedback would be greatly appreciated.
indexPathByAddingIndex adds a new index, it does not increment a value of an index but adds one. If you previously had two indices / dimensions (section and row) you now have 3 indices / dimension: section, row and "the newly added one".
Provides an index path containing the indexes in the receiving index path and another index.
What you should do instead is either create a new NSIndexPath by hand. And I do not think you need to add one, but subtract one, since the item at index 1 should actually be the element in your result at index 0:
let customIndexPath = NSIndexPath(forRow: indexPath.row - 1, inSection: indexPath.section)
which you then use to access the "correct" routine at the right index:
let routine = fetchedResultsController.objectAtIndexPath(customIndexPath) as! SavedRoutines
Your call to tableView.dequeueReusableCellWithIdentifier should stay the same and still pass in the default indexPath.

Find out indexPath of cell

I have a few UITableViewCells with the labels Test1, Test2, Test3, Test4 and Test5. How can I find out the indexPath of the cell which displays Test3? I don't wanna touch the cell or do something like this. Thanks for your answers!
The cells:
Core Data model:
Create a class variable reference to the cell, and then in cellForRow assign the cell with the "Test3" label to your class variable.
var test3Cell: UITableViewCell?
// ...
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = ... // Declaration here
// set up the cell
if cell.textLabel?.text == "Test3" {
self.test3Cell = cell
}
return cell
}
Doing it at the cell level seems backwards — your data model (the table’s UITableViewDataSource) would seem much easier to query.
If you have a reference to the label you can use indexPathForRowAtPoint.
let pointInTable = sender.convertPoint(theLabel.bounds.origin, toView: self.tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(pointInTable)
indexPath where indexPath is an optional.
How are you populating the table? If you are populating the label in the cell of tableview using the content from an NSArray.. you can find the indexpath from the index of the item in the Array obj..
If not, the way i can think of is to loop through the table cells in the tableView
The table view responds to the below methods
func numberOfSections() -> Int
func numberOfRowsInSection(section: Int) -> Int
you can create an NSIndexPath instance using the above information for each row .. NSIndexPath(forRow: , inSection: )
get the UITableviewCell instance using
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
then you can check the text of label in the cell to see if it matches what you need.
This method will check your current visible cells within the tableview and iterate through them looking for the text.
// Swift 1.2
// Store current visable cells in an array
let currentCells = tableView.visibleCells() as Array
// Iterate through cells looking for a match
for cell in currentCells {
println (cell.textLabel?!.text)
if cell.textLabel?!.text == "Test 3" {
println("Test 3 Found")
}
}
// Swift 2.0
// Store current visable cells in an array
let currentCells = tableView.visibleCells
// Iterate through cells looking for a match
for cell in currentCells where cell.textLabel?.text == "Test 3" {
print("Test 3 found")
}

Resources