How to increase number of rows in UITableview at run time? - ios

I want to know whether there is any way to increase the number of rows of a UITableview without calling reload? I dont want to reload entire table just add new rows at run time.
i Know how to insert new row and updates but the row count increases only after calling reload on the UITableView object is there a way out?

try to do something like this:
NSIndexPath *indexPath1 = [NSIndexPath indexPathForRow:0 inSection:1];
//put these indexpaths in a NSArray
[tableView insertRowsAtIndexPaths:array withRowAnimation:UITableViewRowAnimationNone];

In general, I wouldn't worry too much about calling reloadData on a table view. UITableView is actually very efficient at displaying and reusing cells.
Just make sure you're correctly creating cells (using a cell reuse identifier), or else you could be (or rather, are likely) leaking memory, and this isn't efficient (it is processor expense to constantly create new cells, instead of reusing them).
If you are new to using UITableView, or need a refresher on using them, here's a tutorial on it:
http://www.iosdevnotes.com/2011/10/uitableview-tutorial/
There's also a ton of good tutorials (you can likely just click any at random, and it will have an example somewhere within it using UITableView) on Ray Wenderlich's site:
http://www.raywenderlich.com/

Related

What is More reliable way to reload UITableView Data?

I have found 2 functions to reload my rows of UITableView
1st.
[tableView reloadRowsAtIndexPaths:[tableView indexPathsForVisibleRows]
withRowAnimation:UITableViewRowAnimationNone];
2nd.
[tableView reloadData];
What is more specific way to reload UITableView Data
Both are "reliable" and possibly "specific" (depending on what you mean by specific) -- in fact, there are even more ways:
reloadData
reloadRowsAtIndexPaths:withRowAnimation:
reloadSections:withRowAnimation:
reloadSectionIndexTitles
Check the developer documentation (https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/index.html) on the differences, but the short answer is that they are all reliable -- it just depends on how much information you want to reload, and if you want to do it with animation.
For example, if you only want to reload certain rows, you can use reloadRowsAtIndexPaths:withRowAnimation:. If you want to reload a whole section, use reloadSections:withRowAnimation:.
This answer may have two cases,
a) You've the table with at least a single section (with a title or returning a view for section)
In this case, if you call reloadData then it'll also reload section too. But reloadRowsAtIndexPaths:withRowAnimation: will only reload the visible cells and not the section.
b) You've the table with at least a single section (without a title or returning a view for section)
In this case, if you call reloadData or reloadRowsAtIndexPaths:withRowAnimation: both will reload only the visible cells.
So which one is the better?
As I explained, based on the case, you should call any of the one method. If you have sections which you don't want to reload then you should call reloadRowsAtIndexPaths:withRowAnimation: method. Or if you want to reload cells along with section then should call reloadData method.

iOS : How to reload a UITableView with a lot of cells without lagging the App?

I have a lot of cells (around 3000 cells) that I need to reload constantly. I was wondering if there is currently a way to reload it faster without it lagging the App. I do the typical [tableview reloadData]; Any tips or suggestions are appreciated.
Don't implement tableView:heightForRow: in your delegate or it will slow down considerably as it recalculates every row. iOS checks to see if you implement that method and if you define it the OS changes its table height calculation from a simple multiply to a loop over the cells.
Since you have not provided context or code to show where you call [tableview reloadData];, I can only talk in generalities.
I am going to assume in your 3,000 rows possible 20 are displayed at a time.
Here is the sequence of events or actions that needs to occurs
A row gets update
Check if row is visible: indexPathForVisibleRows
If row is not visible, nothing to do
If row is visible, then following actions should be taken
[tableview beginUpdates]
[tableview reloadRowsAtIndexPaths...]
[tableview endUpdates]
Reload only visible cells if you have consistent number of items, otherwise use insert/delete methods to add cells to tableview.
When making a reloadData for your tableView, tableView:heightForRow: delegate function make a height recalculation of every row.
My solution is to save the heights for your cells already calculated (create an NSDictionary that contains all row heights. exp. create a NSDictionary with keys is the id of object that will be show on the cell and the value is the height).
When tableView attempt to recalculate the height of each cell, it will check if we have already a saved entry in your dictionary with this key (id of object), and tableView:heightForRow: will return this value if found.
I am using this solution in my chat app, and I noticed a performance increase.
Good luck.

Middle animation in grouped table view looks horrible

I've found similar question, but there is no answer (sorry, answer just doesn't work).
So I have grouped table and I want to animate content update instead of doing [tableView reloadData].
I do that by using this piece of code:
// Data source already updated here, but reloadData wasn't called
[self.tableView beginUpdates];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationMiddle];
[self.tableView endUpdates];
I uploaded 2 examples of the animation:
Plain: http://cl.ly/3u1M3l1w3V3J (slow motion)
Grouped: http://cl.ly/1O3Z2M280n0z (slow motion)
As you can see difference is huge.
I don't change my code at all, just change tableView style in the storyboard.
Does it men that there is no other way then subclassing UITableView and UITableViewCell and implement my very own animation using CoreAnimation?
Implementing your own animation with CoreAnimation shouldn't be necessary when it comes to animating the rows of the table.
UITableView supports much more advanced animations than simply reloading a section and I suggest that you take a look at them.
Since you are shuffling the rows in your videos you should take a look at moveRowAtIndexPath:toIndexPath: (on UITableView). You put the calls to it within beginUpdates and endUpdates.
By knowing the order before and after the re-shuffle you can move all the rows into their new places and have them slide into their correct place.
It will take some thinking to figure out where each row should go but it will be much easier than rolling your completely custom solution.

TableView reloadData vs. beginUpdates & endUpdates

I got a tricky problem regarding updating my TableView, i get different results using different methods of updating it, let me explain:
Situation 1:
I use [tbl reloadData]; where tbl is my TableView, to update the TableView - works as intended.
Situation 2:
I use:
[tbl beginUpdates];
[tbl reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationRight];
[tbl endUpdates];
Where tbl is my TableView, and indexPaths is an array containing all the indexPaths present in the TableView. Now the array is fine, it contains all the correct indexPaths (double and triple checked) but for some reason - this does not work as intended.
Now I realize that this is an X-Y problem (where I ask for Y but my problem is really X because I think solving Y will solve X) and thats only because I feel it's a bit complicated explaining X (the consequence of said above problem) in an easy way, so I'd rather refrain from that if possible.
So, down to my question: Is there a difference between the two ways of updating the TableView (aside from the animation bit of course) or should I suspect the problem to lay elsewhere?
EDIT:
Okay, I'll try to explain what the symptoms are:
In the cellForRowAtIndexPath-method I add a button to each cell with an assigned tag which is equal to the cell's indexPath row, like such:
btn.tag = indexPath.row;
The reason I do this is so I can identify each button as they all call the same function:
- (void)btnPressed:(id)sender
When I then update the cells - because some values in the cells have changed - Situation 1 makes everything work fine, Situation 2 however - mixes up the tags so the next time one of the buttons are pressed, they no longer have the correct tags.
The mix-up does appear random to me, but the randomization occurs differently depending on which cells button I press first. I hope this clarifies my problem.
From the UITableView documentation
beginUpdates
Begin a series of method calls that insert, delete, or
select rows and sections of the receiver.
That means, you should not use this unless you are inserting, deleting or selecting. You are doing neither of these.
Also, you should end beginUpdates with endUpdates, not reloadData. Documentation:
This group of methods must conclude with an invocation of endUpdates.
The first difference between reloadData and reloadRowsAtIndexPaths is that there are 2 UITableViewCell objects allocated simulteaneosuly for the same indexPath when doing reloadRowsAtIndexPaths (because the tableview 'blends' in the the new cell) . This is sometimes not foreseen by the code in cellForRowAtIndexPath .The surprise comes from the fact that even if a cell was already allocated for a particular cell identfier the table view does not give you back this cell in dequeueReusableCellWithIdentifier when calling reloadRowsAtIndexPaths, instead it returns nil. In contradiction reloadData reuses the cells it already allocated .
The 2nd difference is that endUpdates after reloadRowsAtIndexPaths directly calls cellForRowAtIndexPath (if you set a breakpoint there,endUpdates is visible in the stack trace) whereas reloadData schedules the calls to cellForRowAtIndexPath at a later time (not visible in the stack trace).
However you would need to post a bit more code to give us insight what you are doing there. In principle the indexPaths of the new cells are identical to the old ones also with reloadRowsAtIndexPaths as long as you don't delete or insert rows.
Call this method if you want subsequent insertions, deletion, and selection operations (for example, cellForRowAtIndexPath: and indexPathsForVisibleRows) to be animated simultaneously.
I think this is what you want. beginUpdates & endUpdates can change the UItableview with animation.

Graphical glitches when adding cells and scrolling with UITableView

I am using a UITableView to display the results of a series of calculations. When the user hits 'calculate', I add the latest result to the screen. When I add a new cell, the UITableViewCell object is added to an array (which is indexed by tableView:cellForRowAtIndexPath:), and then I use the following code to add this new row to what is displayed on the screen:
[thisView beginUpdates];
[thisView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation: UITableViewRowAnimationFade];
[thisView endUpdates];
This results in the new cell being displayed. However, I then want to immediately scroll the screen down so that the new cell is the lowermost cell on-screen. I use the following code:
[thisView scrollToRowAtIndexPath:newIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
This almost works great. However, the first time a cell is added and scrolled to, it appears onscreen only briefly before vanishing. The view scrolls down to the correct place, but the cell is not there. Scrolling the view by hand until this invisible new cell's position is offscreen, then back again, causes the cell to appear - after which it behaves normally. This only happens the first time a cell is added; subsequent cells don't have this problem. It also happens regardless of the combination of scrollToRowAtIndexPath and insertRowsAtIndexPath animation settings.
EDIT:
I've now started inserting cells at the second-to-last position of the table, rather than the end, and the problem still occurs - when first inserted, a cell is 'invisible' until it goes offscreen and comes back on again. What could be causing this, and how can I force the cell to be drawn as soon as it is added to the table?
You're having problems because your updating the table without updating the data model backing it. Tables don't actually know how many rows they have nor what cells to display. They depend on the datasource and the delegate to tell them these things. Your design expects the table itself to track them.
insertRowsAtIndexPaths: is intended to be used for moving existing rows around a table, not for adding entirely new logical rows. When you insert an entirely new cell, the tableview looses track of how many rows it actually has.
Before you display a new row, the first thing you should do is update the values returned by:
– numberOfSectionsInTableView:
– tableView:numberOfRowsInSection:
... to reflect the addition of the new rows. This will allow the table to understand how big it is.
Then you need to update cellForRowAtIndexPath: to return the correct cell for the added row. Then you need to reload the table.
After you've done that, you should be able to scroll the tableview to the end and have the cell display properly.
The important thing to remember about tables is that they are dumb. The table itself holds no data, doesn't know how many sections and rows it has or what order the rows and sections come in. All the logic about data, sections, rows, cells and cell contents comes from the datasource and/or the delegate. When you want to change a table, you actually change the datasource and/or the delegate and then the table will reflect those changes automatically.
Edit:
Upon rereading the parent, I see that your putting the actual UITableViewCell objects in your data array and that you have one cell for each row.
This is not how tableviews are supposed to work and this will not scale beyond a few dozen rows at most.
Tableviews are intended to be an illusion that allows you display a lOGICAL table which has an arbitrary high number or rows. To that end, it only keeps enough UITableViewCell objects alive to cover the visually displayed area in the UI. With a default cell height of 44 pixels this means a tableview will never have more than 9 cell objects at a time.
Instead of eating memory holding cells that are not displayed, the tableview lets the delegate dequeue a cell that has scrolled off screen, repopulate it with the data of another LOGICAL row and then display it in a new position. This is done in cellForRowAtIndexPath:
You really need to start over here with your design. Your data needs to be kept separate from the user interface objects. You don't want to have more cells alive at anyone time than absolutely necessary because your memory use will balloon and your response time will degrade. Your current problem is the result of this unusual design.
When you've done that, you can add the result row as outlined above.
Try to scroll with some time shift after cell update via NSTimer or performSelector:withDelay:. It can help but to fix all problems I think there need to do more work.
The glitches may be caused because a UITableView considers itself the owner of any UITableViewCell instances it is displaying, and reuses them as needed. Part of that process is calling prepareForReuse on the cell. Since you are keeping the cells in an array, you do not want them reused. Try implementing an empty prepareForReuse in your UITableViewCell class. Or just create cells dynamically in tableView:cellForRowAtIndexPath: as apple recommends.
I used what Skie suggested to avoid the problem in the following way:
Immediately after adding the row:
[self performSelector:#selector(scrollToDesiredArea:) withObject:newIndexPath afterDelay:0.4f];
This called the following:
-(void)scrollToDesiredArea:(NSIndexPath *)indexPath{
UITableView *thisView = (UITableView*)self.view;
[thisView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
The delay of 0.4s seems to be sufficient to avoid the glitching; any less and it still happens. It may have to be different on varying models of iPhone hardware, though - I only tested on emulator.

Resources