How to get indexpath from row index Swift - ios

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?
}

Related

Trying to remove an element from an array i get this error: Contextual type 'Int' cannot be used with array literal

I added a button for the reloadCell in a tableView and i would like, when the button is pressed, to remove the element of an array on the specific cell but i'm getting this error
Contextual type 'Int' cannot be used with array literal
#IBAction func reloadCell(_ sender: UIButton) {
let index = IndexPath(row: sender.tag, section: 0)
sortedArray.remove(at: [index]) //HERE I GET THE ERROR
self.tableView.reloadRows(at: [index], with: .right)
}
How can i solve it?
Arrays use Ints as indices, not IndexPaths as tableView does:
sortedArray.remove(at: sender.tag)
you can't use an IndexPath type for array (you should use int type)
for ex:
sortedArray.remove(at: index.row)
self.tableView.reloadRows(at: index.row, with: .right)

UICollectionView - insertItems(at: indexPath) not working

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)

How can I know the original cell index of detailed table view?

I implemented searchbar. It works okay when I only search for food. But When I try to edit the food from the search result, it only changes the search result array(foodSearching) but not for the original array(foods). So I need to know the indexpath for the original array(foods) when I edit the table view. Is there any help?
selectedIndexPath is different for 'foodSearching' array (temporary array to show search result) and 'foods' array (original array to hold all of the data)
#IBAction func unwindToFoodList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? FoodViewController, food = sourceViewController.food {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing food.
if(isSearching){
foodSearching[selectedIndexPath.row] = food
// foodSearching array is temporary..
//need to find indexpath for foods array(original array)and update it as well...!!
tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
}else{
foods[selectedIndexPath.row] = food
tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
}
} else {
// Add a new food.
let newIndexPath = NSIndexPath(forRow: foods.count, inSection: 0)
foods.append(food)
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
}
// Save the foods.
saveFoods()
}
}
I would suggest not using indexPath as the reference to data since indexPath can change. If you were using Core Data, I would suggest objectID, but if you're not, I would suggest using any other uniquely identifiable information and use that as the reference. Creating following helper functions can help your implementation:
func indexPath(forFoodWithID uniqueID: IDType) -> NSIndexPath?
func foodID(forFoodAtIndexPath indexPath: NSIndexPath) -> IDType?
I hope that helps.

Animate UITableViewCell gives me error

I have a UITableView. I want to add a cell to the beginning of the tableView with animation. I tried the following:
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
tableView.reloadRowsAtIndexPaths(indexPath, withRowAnimation: UITableViewRowAnimation.Automatic)
But I get the following error:
Cannot invoke 'reloadRowsAtIndexPaths' with an argument list of type
'(NSIndexPath!, withRowAnimation: UITableViewRowAnimation)'
(When I just do tableView.reloadData() it works fine.)
What am I doing wrong, and how can I fix it?
tableView.reloadRowsAtIndexPaths expects an [NSIndexPath] instead of an NSIndexPath! as first argument.
so fix it by calling
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
let indexPaths = [indexPath]
tableView.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.Automatic)
You get the error, because reloadRowsAtIndexPaths expects an array of index paths, not a single index path. However reloadRowsAtIndexPaths wouldn't add a cell, but just reload the cells at the specified index paths.
Instead you should use insertRowsAtIndexPaths to add your cell.

UITableView indexPath of last row

I am attempting to make the last row in a UITableView visible, after it has been added. Right now, when I add a row and call reloadData, the table goes to the top.
I figure if I get the indexPath for the last row, that I can select that row and it should appear in the list. I am unsure of how to get that value, or even if I am approaching this correctly.
How do I get an indexPath for a specific row?
Please note that, you don't need to call the reloadData to make the last row visible. You can make use of scrollToRowAtIndexPath method.
You can use the below code to achieve your goal.
// First figure out how many sections there are
let lastSectionIndex = self.tblTableView!.numberOfSections() - 1
// Then grab the number of rows in the last section
let lastRowIndex = self.tblTableView!.numberOfRowsInSection(lastSectionIndex) - 1
// Now just construct the index path
let pathToLastRow = NSIndexPath(forRow: lastRowIndex, inSection: lastSectionIndex)
// Make the last row visible
self.tblTableView?.scrollToRowAtIndexPath(pathToLastRow, atScrollPosition: UITableViewScrollPosition.None, animated: true)
Swift 4.0:
tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.none, animated: true)
You can use scrollToRowAtIndexPath with extension:
In Swift 3:
extension UITableView {
func scrollToLastCell(animated : Bool) {
let lastSectionIndex = self.numberOfSections - 1 // last section
let lastRowIndex = self.numberOfRows(inSection: lastSectionIndex) - 1 // last row
self.scrollToRow(at: IndexPath(row: lastRowIndex, section: lastSectionIndex), at: .Bottom, animated: animated)
}
}
Shamsudheen TK's answer will crash
if there is no rows/sections in tableview.
The following solution to avoid crash at run time
extension UITableView {
func scrollToBottom() {
let lastSectionIndex = self.numberOfSections - 1
if lastSectionIndex < 0 { //if invalid section
return
}
let lastRowIndex = self.numberOfRows(inSection: lastSectionIndex) - 1
if lastRowIndex < 0 { //if invalid row
return
}
let pathToLastRow = IndexPath(row: lastRowIndex, section: lastSectionIndex)
self.scrollToRow(at: pathToLastRow, at: .bottom, animated: true)
}
}
Note: If you are trying to scroll to bottom in block/clousure then you need to call this on main thread.
DispatchQueue.main.async {
self.tableView.scrollToBottom()
}
Hope this will helps other
As suggested by others get indexPath for perticular sections like section 0.
After that call...add this methos in cellFOrROwAtIndex
[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES]; ..to scroll to specific indexPath in TableView.
Note:-But it still need scrolling of tableview in Downward direction.
You shouldn't be using -reloadData for this use case. What you're looking for is -insertRowsAtIndexPaths:withRowAnimation:.
Feel free to ask if you want some usage examples or a more detailed explanation as to why using -reloadData send you to the top of the UITableView.
Not mandatorily required to get the indexpath of last row.
You can set the CGPoint of UITableview to show a last row you added.
I always use this code in my chat application to show a last added message.
//Declaration
#IBOutlet weak var tableview: UITableView!
//Add this executable code after you add this message.
var tblframe: CGRect = tableview.frame
tblframe.size.height = self.view.frame.origin.y
tableview.frame = tblframe
var bottomoffset: CGPoint = CGPointMake(0, tableview.contentSize.height - tableview.bounds.size.height)
if bottomoffset.y > 0 {
tableview.contentOffset = bottomoffset;
}
I hope it will work for you.
Thanks.
Swift 5.0 +
extension UITableView {
func isLastVisibleCell(at indexPath: IndexPath) -> Bool {
guard let lastIndexPath = indexPathsForVisibleRows?.last else {
return false
}
return lastIndexPath == indexPath
}
}

Resources