Find out last added UITableViewCell - ios

I'd like to find out the last UITableViewCell in the last position of my UITableView and change the detailTextLabel of this cell into "Last added". Does someone knows how to do this in Swift?

tableView gives you all you need to create last rows's indexPath and get it's cell.
let lastSectionIndex = tableView.numberOfSections()-1
let lastSectionLastRow = tableView.numberOfRowsInSection(lastSectionIndex) - 1
let indexPath = NSIndexPath(forRow:lastSectionLastRow, inSection: lastSectionIndex)
let cell = tableView.cellForRowAtIndexPath(indexPath)
but depending on your needs you might be better accessing the cell in tableView's delegate
func tableView(_ tableView: UITableView,
willDisplayCell cell: UITableViewCell,
forRowAtIndexPath indexPath: NSIndexPath)
Something like
let lastSectionIndex = tableView.numberOfSections()-1
let lastSectionLastRow = tableView.numberOfRowsInSection(lastSectionIndex) - 1
let lastIndexPath = NSIndexPath(forRow:lastSectionLastRow, inSection: lastSectionIndex)
let cellIndexPath = tableView.indexPathForCell(cell)
if cellIndexPath == lastIndexPath {
// the last cell is about to be displayed. change get here.
}

Since table views always require a datasource, you should look there instead of the table view.
NSIndexPath *lastCellIDP = [NSIndexPath indexPathForRow:self.dataSource.lastObject inSection:0];
UITableViewCell *lastCell = [self.tableView cellForRowAtIndexPath:lastCellIDP];
lastCell.detailTextLabel.text = #"U NEED 2 ChAnGE!";
That code assumes your datasource is of type NSArray.
That said, I would put this logic inside a UITableViewCell subclass when you bind the data, or possibly the property on your model of your datasource if it's the last item.

Related

In Swift, I'd like the last 3 sections have only labels in their rows (and NOT text fields)

In my Swift project I have a table controller view with a table view inside. The table view is divided into 4 section and every section has 4 rows. Currently, each row is formed by a label beside a text field.
My purpose is that only rows in the first section has label beside text field. On the contrary, I want the last 3 sections have only labels in their rows (and NOT text fields).
Please, help me.
That's the code I wrote to manage with this problem but it's not working:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
print("index ", indexPath.section);
let cell = tableView.dequeueReusableCellWithIdentifier("tableCell", forIndexPath: indexPath)
cell.textLabel?.text = self.items[indexPath.section][indexPath.row];
if(indexPath.section == 0){
let cell = tableView.dequeueReusableCellWithIdentifier("tableCell") as! TextInputTableViewCell
cell.configure("", placeholder: "name")
}
return cell
}
It's not working because you are using the same cell for all the rows. You need to define two different rows. You can do this by setting prototype cells to more than one row (two in your case).
Each cell must have its own reuse identifier and it must be unique within that table view.
Then in your tableView(cellForRowAtIndexPath:) you can ask:
if indexPath.section == 0 {
cell = tableView.dequeueReusableCellWithIdentifier("firstSectionCell", forIndexPath: indexPath)
//
} else {
cell = tableView.dequeueReusableCellWithIdentifier("otherSectionCell", forIndexPath: indexPath)
//
}
Also note that in Swift you do not need to use parenthesis in if-statement (nor for, while, etc). So I suggest you remove them as they are pointless.
It looks like your cell in if(indexPath.section == 0) block doesn't actually have scope outside that block so any properties set there won't get returned there. If you just want to remove the textField, but keep the label, You can just set the textField.hidden = true. Here is how I would go about it.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("tableCell", forIndexPath: indexPath) as! TextInputTableViewCell
cell.textLabel?.text = self.items[indexPath.section][indexPath.row];
if(indexPath.section == 0){
cell.textField.hidden = false //Assumes TextInputTableViewCell's textField property is called "textField"
cell.configure("", placeholder: "name")
} else {
cell.textField.hidden = true //Not in first section we will hide textField
}
return cell
}
Doing it this way you can use the same cell class for every cell in your tableView, but just hide what you want based on its section.

Reset indexPath for uicollectionview

I have a collection view which looks like
1st cell will hold some text , and from 2nd cell onwards data is populated from a data manager
To handle this i have code something like this
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
if(indexPath.row==0){
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(customCellNibName, forIndexPath: indexPath)
let offer: TGOfferDataSource? = dataSource?.dataforIndexPath(indexPath) as? TGOfferDataSource
cell.setOfferData(offer!)
return cell
}
else if(indexPath.row != 0)
{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellNibName, forIndexPath: indexPath)
let product: TGProductDataSource? = dataSource?.dataforIndexPath(indexPath) as? TGProductDataSource
cell.setProductData(product!)
return cell
}
}
The issue is 1st product from data manager is hidden behind this grey cell.
So i figure i will have to increment index path in elseif part. Something like indexPath in elseif part should also starts from 0.
(Swift and cells are xib)
Any idea how to do it ?
Thanks
Had a look at this but how to do this in swift
EDIT
If I remove if part , it looks like
You can create new NSIndexPath in swift using following
let path = NSIndexPath(forRow: indexPath.row - 1, inSection: indexPath.section)
And you can pass above path to your dataforIndexPath in the else part. Hope that helps!

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.

reloadRowsAtIndexPaths doesn't update my cell data

I have a UITableView in my ViewController.
One of the cell could be tap into another TableViewController to allow select a value.
I want to update my cell after back from the callee ViewController.
right now, i could pass back the selected value by delegate.
However, i tried following way, none of them works.
self.mainTable.reloadData()
dispatch_async(dispatch_get_main_queue()) {
self.mainTable.reloadData()
}
self.mainTable.beginUpdates()
self.mainTable.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.None)
self.mainTable.endUpdates()
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
was called and executed without error.
but the UI just doesn't change
here is the way I update value in cellForRowAtIndexPath
if let currentCell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell! {
currentCell.textLabel?.text = address
return currentCell
}
Here is my cellForRowAtIndexPath -
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let id = "Cell"
println(indexPath)
if indexPath.row == 1 {
var cell = tableView.dequeueReusableCellWithIdentifier(id) as? UITableViewCell
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: id)
cell?.contentMode = UIViewContentMode.Center
cell?.selectionStyle = UITableViewCellSelectionStyle.None
cell?.contentView.addSubview(mapView!)
}
return cell!
}else{
let cell = UITableViewCell()
cell.textLabel?.text = self.address
return cell
}
}
Here is the delegate method -
func passBackSelectedAddress(address: String) {
self.address = address
var indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.mainTable.beginUpdates()
self.mainTable.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
self.mainTable.endUpdates()
}
My fix:
After more debug, i find the cause,
the self.address value is updated in delegate, however it roll back to previous value in cellForRowAtIndexPath.
I change the property to a static property, then resolve the problem.
I'm not sure what's wrong with instance property, and why it reverses back.
static var _address:String = ""
It seems like you're trying to grab a cell from the UITableView and then update the textLabel value that way. However, UITableView and UITableViewCell are not meant to be updated in this way. Instead, store the value of address in your class and update this value when the delegate calls back into your class. If cellForRowAtIndexPath constructs the UITableViewCell with the value of self.address, calling mainTable.reloadData() after should update the cell to the new value.
For example:
var address: String
func delegateCompleted(address: String) {
self.address = address
self.mainTable.reloadData()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(<your identifier>)
if (indexPath == <your address cell indexPath>) {
let textLabel = <get your textLabel from the cell>
textLabel?.text = self.address
}
return cell
}
Your cellForRowAtIndexPath has some problems -
You are using the same re-use identifier for different types of cell (one with a map, one without)
When you allocate the table view cell for the other row, you don't include the re-use identifier.
You have no way of referring to the map view that you are adding after the method exits because you don't keep a reference.
If you are using a storyboard then you should create the appropriate prototype cells and subclass(es) and assign the relevant cell reuse ids. If you aren't then I suggest you create a cell subclass and register the classes against the reuse identifiers. Your cellForRowAtIndexPath will then look something like -
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var returnCell:UITableViewCell
if indexPath.row == 1 {
var myMapCell = tableView.dequeueReusableCellWithIdentifier("mapCell", forIndexPath:indexPath) as MYMapCell
myMapCell.contentMode = UIViewContentMode.Center
myMapCell.selectionStyle = UITableViewCellSelectionStyle.None
// Set the properties for a map view in the cell rather than assigning adding an existing map view
returnCell=myMapCell
}else{
returnCell = tableView.dequeueReusableCellWithIdentifier("addressCell", forIndexPath:indexPath)
returnCell.textLabel?.text = self.address
}
return returnCell;
}

How can I specify both section and row in the indexPath call to my array?

Previously, I was populating my TableView by using this code:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cellIdentifier = "Cell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath:
indexPath) as CustomTableViewCell
let entry = entries[indexPath.row]
cell.entryLabel!.text = entry.labelOne
cell.entryDayLabel!.text = entry.day
cell.entryDateLabel!.text = entry.date
return cell
}
I've since added sections to my Table, and I'm having trouble figuring out how to specify both the section and the row in this call to my array.
I've tried
let entry = entries[indexPath.section]
and I've tried
let entry = entries[indexPath.row + indexPath.section]
But neither work correctly.
Is there a proper way to do this that I'm missing? Any help is greatly appreciated. Thanks!
indexPath.section
will return the current section.
What you usually want to do then, is add some conditional logic like:
if(indexPath.section == 0)
{
entry = entries[indexPath.row]
}
else if(indexPath.section == 1)
{
entry = whateverDataSource[indexPath.row]
}
Are you using two different tables and/or cell types for your sections?

Resources