UITableView Insert More Rows While Keeping The Already Fetched Ones - iphone

Below is the function that i am using to get and parse different pages of the feed.
- (void)getFeed:(NSInteger) pageNumber
{
collectedData = [[NSMutableData alloc] init];
NSString *urlStr =[NSString stringWithFormat:#"http://sitename.com/feed/?paged=%d", pageNumber];
NSURL *url = [NSURL URLWithString:urlStr];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:YES];
}
When user scrolls down to the bottom of tableview, i use the below function to access the second page of feed and so on by incrementing the counter:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
float endScrolling = scrollView.contentOffset.y + scrollView.frame.size.height;
if (endScrolling >= scrollView.contentSize.height)
{
counter ++;
[self getFeed:counter];
}
}
The issue is when instead of loading it below the already fetched items, it reloads the tableview to show the new page items and old ones disappear. I want to load new page items below the existing ones to have infinite scroll. How can i do that?

It feels bad to answer your own question, but taking help from another forum, i was able to figure out the answer.
The tableview controller was not in sync with the mutable array, so i created another and append both arrays.
Step1: I created a property
#property (nonatomic, readonly) NSMutableArray *moreItems;
Step2: Initialised the array in ViewDidLoad before calling fetchEntries
moreItems = [[NSMutableArray alloc] init];
Step3: In connectionDidFinishLoading, i appended new array 'moreItems' to the previous array 'items'
[moreItems addObjectsFromArray:[channel items]];
Step4: Changed the data source in numberOfRowsInSection from
return [[channel items] count];
to new array
return [moreItems count];
and then in cellForRowAtIndexPath, used the new array
RSSItem *item = [moreItems objectAtIndex:[indexPath row]];

You should use insertRowsAtIndexPaths: withRowAnimation: method of UITableView.
One thing you must take care of is that when you use this method, number rows retuned by tableView:numberOfRowsInSection should be equal to number_of_existing_rows + number_of_newly_inserted_rows

After you finish retrieving the new feed for next page get the objects in an array.
Add these objects from this array to your data source array.
Now insert rows to the table as follows:
[yourTable beginUpdates];
[yourTable insertRowsAtIndexPaths:indexPathsToInsert
withRowAnimation:UITableViewRowAnimationBottom];
[yourTable endUpdates];
where "indexPathsToInsert" is array of NSIndexPath objects starting from the last row in your table and containg indexPaths for your new objects.
Hope this helps.

Hi,
Create a for loop, with the starting index is (number of objects in datasource) and number of objects you want to add (count),create the array of indexpaths and call insertRowsAtIndexpaths:withRowAnimation. I hope the code helps you.
NSInteger statingIndex = [self.tableViewDatasource count];
NSInteger noOfObjects = //Get the no.of objects count from getFeed method.
NSMutableArray *indexPaths = [[NSMutableArray alloc] init];
for (NSInteger index = statingIndex; index < statingIndex+noOfObjects; index++) {
[_objects addObject:]; // add the object from getFeed method.
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
[indexPaths addObject:indexPath];
[indexPath release];
}
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];

Whenever your array gets a new object added you call this
-(void)insertRowInTableView
{
int row = [YourDataSourceArray count];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
[YourTableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationTop];
}
Here YourDataSourceArray is the array from which you are setting your tableview row count/data.
YourTableView is your tableView object name.

This may be because when ever you are getting your data from the webservice you are adding it in the Array you are using to populate the table, by this you will have only new data, what you have to do is you have to append the new data to the array(Make it Mutable Array and append new data) and just use the following line
[your_Table reloadData];
Hope this will help you.

Instead of using scrolling down to load, better use the last row of tableview as "Load More Items" and when the user selected that row, get the new feed and reload the tableview.
And also I have one doubt in your code, everytime in getfeed method, you are creating array
collectedData. I doubt you missing of appending this data to table datasource data,which can cause to show you only recent feed data in tableview. Please check

Related

App crashes when refreshing table (Probably the call endUpdates on tableView causes the crash)

Came across this post but did not fix my problem.
iPhone app crashing on [self.tableView endUpdates]
Having a UITableView which loads articles from a newspaper website. First load works
as expected but when I use an UIRefreshControl to fetch the articles again my app crashes
when (animating) inserting rows.
Error:
Code:
- (void)insertRowsWithAnimation
{
NSMutableArray *indexPaths = [NSMutableArray array];
NSInteger i = self.latestArticlesArray.count - self.latestArticlesArray.count;
for (NSDictionary *dict in self.latestArticlesArray) {
[indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
i++;
}
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationMiddle];
[self.tableView endUpdates];
}
- (void)fetchEntries
{
UIView *currentTitleView = [[self navigationItem] titleView];
UIActivityIndicatorView *aiView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
[[self navigationItem] setTitleView:aiView];
[aiView startAnimating];
void (^completionBlock) (NSArray *array, NSError *err) = ^(NSArray *array, NSError *err) {
if (!err) {
[[self navigationItem] setTitleView:currentTitleView];
self.latestArticlesArray = [NSArray array];
self.latestArticlesArray = array;
[self insertRowsWithAnimation];
}
};
[[Store sharedStore] fetchArticlesWithCompletion:completionBlock];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.latestArticlesArray.count;
}
If you want to see more methods please let me know. Hope you can help. From what I've learned so far I think the number of posts have changed therefore the table expects another amount of articles to show?
The line
NSInteger i = self.latestArticlesArray.count - self.latestArticlesArray.count;
sets i to zero, therefore insertRowsAtIndexPaths is called with an empty array.
I assume that your intention was to call insertRowsAtIndexPaths with the row numbers of the newly added rows, but then you have to remember the old data before replacing the array in
self.latestArticlesArray = array;
But note that since you replace the entire array, you can as well call
[self.tableView reloadData];
instead of beginUpdates/insertRowsAtIndexPaths/endUpdates.
Update: My first analysis is wrong (and wattson12's is correct). As you said in the comments, you need just a simple animation that removes all previous rows and inserts the new rows after a fetch. This can be done like this:
- (void)replaceArticlesWithAnimation:(NSArray *)articles
{
NSUInteger oldCount = [self.latestArticlesArray count];
self.latestArticlesArray = articles;
NSUInteger newCount = [self.latestArticlesArray count];
[self.tableView beginUpdates];
for (NSUInteger i = 0; i < oldCount; i++) {
[self.tableView deleteRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:i inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];
}
for (NSUInteger i = 0; i < newCount; i++) {
[self.tableView insertRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:i inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];
}
[self.tableView endUpdates];
}
and in fetchEntries you call
if (!err) {
[[self navigationItem] setTitleView:currentTitleView];
[self replaceArticlesWithAnimation:array];
}
The reason it loads the first time is because you are inserting a number of rows each time, so on the first run the number of rows changes from 0 to the count in array (in the completion block), and you call insertRows with a number of index paths equal to the count of the array.
The 2nd time you call it you are inserting new rows, but you are not updating the count to reflect the new sum. You should be adding your existing array to the one returned in the completion block, and returning the count of that combined array in numberOfRowsInSection
dispatch_async(dispatch_get_main_queue(), ^(void){
//Run UI Updates
});

Accessing String from NSIndex

I currently have a UITableView that is populated by a .plist full of exercises. What I want to be able to do is access individual exercises within the table by storing each exercise that is clicked on into an array, that will later be used to populate a seperate UITableView.
How exactly do I get access to these individual cells so that I can store them into this array. Here is what I have so far:
-(IBAction) saveWorkout {
NSMutableArray *workout = [NSMutableArray arrayWithCapacity:10];
[workout addObject: ] // I'm assuming this is where I add a cell to an array (or atleast the cell's string).
}
Any help?
Without delving too much into the actual code part of your question, calling -cellForRowAtIndexPath to retrieve a title is (can be) extremely expensive, especially if it is called multiple times. Use -didSelectRowAtIndexPath: to get the index of the title within your datasource array, then add that object to your list. Call -saveWorkout when you are finished/reach a certain limit.
A same might look like:
-(void)didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
//other code and such...
//get the index of the object in our original array and add the corresponding object to the new array.
[customWorkoutArray addObject:[workoutArray objectAtIndex:indexPath.row]];
}
To restate #CodaFi in code:
#property (strong, nonatomic) NSMutableArray *selectedElements;
#synthesize selectedElements=_selectedElements;
- (NSMutableArray *)selectedElements {
if (!_selectedElements) {
_selectedElements = [NSMutableArray array];
}
return _selectedElements;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
id element = [self.myModel objectAtIndex:indexPath.row];
// this is the key: this array will now be the basis for your subsequent table of selected items
[self.selectedElements addObject:element];
// do something to your model here that indicates it's been selected
// have your cellForRow... method interrogate this key and draw something special
[element setValue:[NSNumber numberWithInt:1] forKey:#"selected"];
// reload that row so it will look different
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
-(void)didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
You can get the index here by indexPath.row
}

Clear NSMutableArray for a refresh

I have a couple of NSMutableArrays which i need to clear when refreshing the view. However, when I try to clear them with [array removeAllObjects]; my tableview crashes due to index beyond bounds error. All i do with the refresh, is clear the arrays and call the same function as in viewDidLoad for filling the tableview. [tableView reloadData] doesn't get called until the very last line of the method.
EDIT: It's highly likely that the issue is this: I use a pull to refresh external lib, and when you scroll up and release the table, it bounces back down and thus the UITableView tries to load the next cell, which it cant because the array is cleared, and it's still being loaded.
Answer: removeAllObjects from the arrays, immediately do a self.tableView reloadData and then continue with the rest.
problem might be due to numberOfRowsInSection returning some count and your data source array is empty.
just call [array removeAllObjects] and in numberOfRowsInSection return [array count].
I hope it will resolve your issue. Best of Luck!!!
I delete cells from my table view in the following manner-
NSMutableArray* indexPathsToDelete = [[NSMutableArray alloc] init];
for(unsigned int i = 0; i < [self.array count]; i++)
{
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:0]];
}
[self.tableView beginUpdates];
[self.array removeAllObjects];
[self.tableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:UITableViewRowAnimationLeft];
[self.tableView endUpdates];
[indexPathsToDelete release];
When you refresh your array, first check if it has the object or not & then reinitialize your array and release one.
What i did was
[array removeAllObjects];
then call
[self.tableview reloadData];

UITableViewCellAccessory check

I have an array which loads in table view, and if users taps a certain cell it changes to UITableViewCellAccessoryCheckmark.
How can I check what object in array is checked and add all objects that are checked to another array?
If you want a function that actually gets the checked objects at a whim, use the following:
- (NSMutableArray*)checkedObjectsInTable:(UITableView*)tableView
{
NSMutableArray *checkedObjects = [[[NSMutableArray alloc] init] autorelease];
for (int i=0; i<tableDataSource.count; i++)
{
if ([tableView cellForRowAtIndexPath:
[NSIndexPath indexPathForRow:i inSection:0]].accessoryType == UITableViewCellAccessoryCheckmark)
{
[checkedObjects addObject:[tableDataSource objectAtIndex:i]];
}
}
return checkedObjects;
}
That would allow you to get the data on demand. Note that it would be much less efficient than simply using Jasarien's method, yet there are some situations where it is a better solution.
Something like this in your tableView:didSelectRowAtIndexPath: method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//set checkmark accessory on table cell ...
// get object and add to checkedObjects array
NSInteger index = [indexPath row];
MyObject *object = [myArray objectAtIndex:index];
[checkedObjects addObject:object];
}

How to insert a row to a UITableView?

I want to use insertRowsAtIndexPaths method to insert a row to a UITableView, but I don't know how to make "indexPaths" property for this method.
Please help me!
You can create single NSIndexPath using
+ (NSIndexPath *)indexPathForRow:(NSUInteger)row inSection:(NSUInteger)section; // defined in UITableView.h
Your code to create and fill indexPaths paramater may look like:
NSMutableArray* indexPaths = [NSMutableArray arrayWithCapacity:10];
for (...) // iterate to get all indexes and section you need
{
[indexPaths addObject:[NSIndexPath indexPathForRow:someRow inSection:someSection]];
}
//indexPaths is ready to use
- (NSIndexPath *) indexPathForRow:(NSUInteger)row andSection:(NSUInteger)section {
NSUInteger _path[2] = {section, row};
NSIndexPath *_indexPath = [[NSIndexPath alloc] initWithIndexes:_path length:2];
return [_indexPath autorelease];
}