UICollectionView is not reloading data properly - ios

I'm using a UICollectionView to display some data received from a remote server and I'm making two reloadData calls in a short time, first one to show a spinner (which is actually a UIActivityIndicatorView in a UICollectionViewCell) and the second one after the data was retrieved from server. I store the models based on which the collection view cells are created in a self.models NSArray and in cellForItemAtIndexPath I dequeue cells based on this model. My issue is that when I call reloadData after the request was completed (the second time) the collection view seems to be still in the process of creating the cells for the first reloadData call and it shows just the first two cells and a big blank space in place where the rest of the cells should appear.
I wrote some logs to get some insights of what's happening and the current workflow is:
I'm populating self.models with the model for the spinner cell and then I call reloadData on the UICollectionView.
numberOfItemsInSection is called, which is just returning [self.models count]. The returned value is 2, which is fine (one cell which acts like a header + the second cell which is the one with the UIActivityIndicator inside).
I'm making the request to the server, I get the response and I populate self.models with the new models received from the remote server (removing the model for the spinner cell but keeping the header cell). I call reloadData on the UICollectionView instance.
numberOfItemsInSection is called, which is now returning the number of items retrieved from the remote server + the model for the header cell (let's say that the returned value is 21).
It's just now when cellForItemAtIndexPath is called but just twice (which is the value returned by numberOfItemsInSection when it was called first).
It seems like the collection view is busy with the first reloadData when I call this method for the second time. How should I tell the collection view to stop loading cells for the first reloadData call? I tried with [self.collectionView.collectionViewLayout invalidateLayout] which seemed to work but the issue reappeared, so it didn't did the trick.
Some snippets from my code:
- (void)viewDidLoad {
[super viewDidLoad];
[ ...... ]
self.models = #[];
self.newsModels = [NSMutableArray array];
[self.collectionView.collectionViewLayout invalidateLayout];
[self buildModel:YES]; // to show the loading indicator
[self.collectionView reloadData];
[self updateNewsWithBlock:^{
dispatch_async(dispatch_get_main_queue(), ^{
[self.collectionView.collectionViewLayout invalidateLayout];
[self buildModel:NO];
[self.collectionView reloadData];
});
}];
}
- (void) buildModel:(BOOL)showSpinnerCell {
NSMutableArray *newModels = [NSMutableArray array];
[newModels addObject:self.showModel]; // self.showModel is the model for the cell which acts as a header
if ([self.newsModels count] != 0) {
// self.newsModels is populated in [self updateNewsWithBlock], see below
[newModels addObjectsFromArray:self.newsModels];
} else if (showSpinnerCell) {
[newModels addObject:[SpinnerCellModel new]];
}
self.models = [NSArray arrayWithArray:newModels];
}
- (void) updateNewsWithBlock:(void (^)())block {
// Here I'm performing a GET request using `AFHTTPSessionManager` to retrieve
// some XML data from a backend, then I'm processing it and
// I'm instantiating some NewsCellModel objects which represents the models.
[ ..... ]
for (NSDictionary *item in (NSArray*)responseObject) {
NewsCellModel *model = [[NewsCellModel alloc] init];
model.itemId = [item[#"id"] integerValue];
model.title = item[#"title"];
model.headline = item[#"short_text"];
model.content = item[#"text"];
[self.newsModels addObject:model];
}
block();
}

Related

UITableview delegate and datasource called twice

I struggle with tableview datasource and delegate methods. I've a tableview if user navigate to that table view controller I'm calling web service method which is block based service after the request finished successfully reloaded tableview sections but tableView:cellForRowAtIndexPath:indexPath called twice. Here's my code.
viewDidLoad
[[NetworkManager sharedInstance].webService getValues:self.currentId completion:^(NSArray *result, BOOL handleError, NSError *error) {
self.data = result[0];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 4)] withRowAnimation:UITableViewRowAnimationAutomatic];
});
}
but cellForRowAtIndexPath self.data value is null in the first time.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"%#", self.data); // first time it print null
}
So is there any ideas about this? Thank you so much!
Did you initialise the data array in viewDidLoad? If you didn't it will return null.
if you want to avoid two calls to the tableview try this:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if(!data)
return 0;
return yourNumberOfSections;
}
The tableview calls cellForRowAtIndexPath when it needs to render a cell on screen. If the tableview appears before it has any data, self.data will be empty.
In viewDidLoad set [[self.data = NSMutableArray alloc] init] (for example) and in the datasource/delegate methods of UIITableView it should correctly return numberOfRows etc as zero until your web service populates your data.
It sounds like -tableView:cellForRowAtIndexPath: is being called simply because the view is loaded and the table is trying to populate itself before you've received any data from your web service. At that point, you won't have set self.data (whatever that is) to any useful value, so you get null instead. When your web service returns some data, the completion routine causes the relevant sections to be reloaded, and the table draws the data.

How do I delay execution of a delegate method of one protocol until the delegate method of another protocol has completed execution?

In the .m file ClassroomCollectionViewController, I have the following instance variable declared:
#implementation ClassroomCollectionViewController
{
NSMutableArray *students;
}
This array is populated in the following delegate method of the NSURLConnectionDataDelegate protocol, which ClassroomCollectionViewController implements.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (connection == _getStudentsEnrolledInClassConnection)
{
// Parse the JSON that came in
NSError *error;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:_receivedData options:NSJSONReadingAllowFragments error:&error];
if (jsonArray.count > 0)
{
students = [[NSMutableArray alloc] init];
// Populate the students array
for (int i = 0; i < jsonArray.count; i++)
{
Student *studentInClass = [Student new];
studentInClass.name = jsonArray[i][#"name"];
studentInClass.profile = jsonArray[i][#"profile"];
studentInClass.profileImageName = jsonArray[i][#"profile_image_name"];
[students addObject:studentInClass];
}
}
}
}
In the following delegate method of another protocol, namely the UICollectionViewDelegate, the students array populated above is used to construct the individual cells of the collection view.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
ClassmateCollectionViewCell *myCell = [collectionView
dequeueReusableCellWithReuseIdentifier:#"ClassmateCell"
forIndexPath:indexPath];
UIImage *image;
long row = [indexPath row];
image = [UIImage imageNamed:[students[row] profileImageName]];
myCell.imageView.image = image;
myCell.classmateNameLabel.text = [students[row] name];
return myCell;
}
The problem is that the students array is not yet populated by the time that the second of the two delegate methods above executes, which results in there being no data for the cells in the collection view to display.
The obvious solution to this problem is to delay the execution of the second method until the first one has finished executing (thus ensuring that the students array will be populated by the time the cells in the collection view are constructed). But I couldn't for the life of me figure out how to make this so in this particular context - since I have no control over when the second delegate method is invoked. I have considered using blocks and multithreading in order to solve this, but have failed at coming up with a solution that is relevant for this specific problem.
Could anyone point me in the right direction?
Thanks very much,
Jay
Try this, connect an IBOutlet to collection view and
in your connectionDidFinishLoading: method
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//All other codes for populating `students` array
[self.collectionView reloadData];
}
To delay execution of the collection view, you need to wait until the connectionDidFinishLoading calls.
Its pretty simple you can reload collection view from connectionDidFinishLoading method.
Thats it.
This is design problem. It suppose to go like this (MVC patern):
some service is receiving data by NSURLConnectionDataDelegate sends results to model.
model enforces main thread, updates its data and posts some notification
view controller is registered for model notification and properly reacts on those notifications. Like [self.collectionView reloadData]; or [self.collectionView insertItemsAtIndexPaths: indexPaths]; and so on.

Loading data to use in UITableView cellForRowAtIndexPath

I have a UITableView and I am populating it with image data. This data is loaded fine, but when the records increase in number (e.g. more than 50) the app starts to have problems like freezes and more. I understood that this is the line in my cellForRowAtIndexPath that is causing the issue:
NSData* data = [DKStoreManager loadFileWithName:[finalArray objectAtIndex:indexPath.row] forFolderNumber:[_FolderCode intValue] forUser:[_PassedUserID intValue] andType:_FolderType];
NSDictionary *myDictionary = (NSDictionary*) [NSKeyedUnarchiver unarchiveObjectWithData:data];
I understood it because when I loaded the data only once in the viewDidLoad an used myDictionary as a global variable, then all the cells where logically be the same, but the table scrolled fine fine and the app doesn't crash. finalArray is an array with the names of the files ordered in alphabetical order and the number of rows corresponds to its count. Can anyone suggest a way to load this data outside of the cellForRowAtIndexPath method? How do I then pass everything on to the cellForRowAtIndexPath if all of the NSData are different?
What I have tried to do:
1) I tried to subclass the UITableViewCells and load the data from a method:
cell.FileName = [finalArray objectAtIndex:indexPath.row];
cell.PassedUserID = _PassedUserID;
cell.FolderCode = _FolderCode;
cell.FolderType = _FolderType;
[cell loadContents];
I made sure using a BOOL that loadContents runs only once in the subclass. When I scroll down or up, cells change position. Its a mess...
2) I noticed that if I remove the
if (cell == nil) {
and stop reusing the cell, there are no issues with the cells changing place, but there are huge loading time issues
2) Moving everything in the if (cell == nil) { method, the cells still change place on scroll but the scroll is faster...
4) Loading all the data in the viewDidLoad displaying a "loading..." but the loading is really slow, it doesn't really work out.
5) Using dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ on the data load, but doesn't work, the table scrolls really slow...
PS ,myDictionary contains the image and the name of the cell.
EDIT:
All the data is saved locally, the method "loadFileWithName" loads a saved file in the documents directory.
Please see example of lazy-loading table below.
This example provides a table view with infinite number of cells; data for each cell is loaded in a background thread only when all of the following conditions are true:
UITableView requests that cell;
Cell that data is requested for is visible;
UITableView is not scrolling OR scrolling is not decelerated (i.e. scrolling is performed while user’s finger touches the screen).
That is, excessive load of the system during fast scrolling is eliminated, and data is loaded only for cells that user really needs to see.
In this example, time-consuming and thread-blocking data loading operation is emulated for each cell with sleeping a background thread for 0.2 seconds. To use this example in your real application please do the following:
Replace implementations of the tableElementPlaceholder getter;
In the performActualFetchTableCellDataForIndexPaths: method, replace the following line with your actual loading cell data:
[NSThread sleepForTimeInterval:0.2]; // emulation of time-consuming and thread-blocking operation
Tune implementation of the tableView:cellForRowAtIndexPath: method for your cells implementation.
Please note that any object data needed in the loading code should be thread-safe since loading is performed in non-main thread (i.e. atomic properties and probably NSLock should be used inside the performActualFetchTableCellDataForIndexPaths: method in your time-consuming thread blocking code replacing the [NSThread sleepForTimeInterval:0.2] call).
You can download the full Xcode project from here.
#import "ViewController.h"
#import "TableViewCell.h"
static const NSUInteger kTableSizeIncrement = 20;
#interface ViewController () <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, strong) NSMutableArray* tableData;
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property (nonatomic, readonly) id tableElementPlaceholder;
#property (nonatomic, strong) NSTimer* tableDataLoadDelayTimer;
- (void)fetchTableCellDataForIndexPath:(NSIndexPath*)indexPath;
- (void)performActualFetchTableCellDataForIndexPaths:(NSArray*)indexPaths;
- (void)tableDataLoadDelayTimerFired:(NSTimer*)timer;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableData = [NSMutableArray arrayWithCapacity:kTableSizeIncrement];
for (NSUInteger i = 0; i < kTableSizeIncrement; i++) {
[self.tableData addObject:self.tableElementPlaceholder];
}
// Do any additional setup after loading the view, typically from a nib.
}
- (id)tableElementPlaceholder
{
return #"";
}
- (void)fetchTableCellDataForIndexPath:(NSIndexPath*)indexPath
{
if (self.tableView.decelerating && !self.tableView.tracking) {
if (self.tableDataLoadDelayTimer != nil) {
[self.tableDataLoadDelayTimer invalidate];
}
self.tableDataLoadDelayTimer =
[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(tableDataLoadDelayTimerFired:)
userInfo:nil
repeats:NO];
} else {
[self performActualFetchTableCellDataForIndexPaths:#[indexPath]];
}
}
- (void)tableDataLoadDelayTimerFired:(NSTimer*)timer
{
[self.tableDataLoadDelayTimer invalidate];
self.tableDataLoadDelayTimer = nil;
NSArray* indexPathsForVisibleRows = [self.tableView indexPathsForVisibleRows];
[self performActualFetchTableCellDataForIndexPaths:indexPathsForVisibleRows];
}
- (void)performActualFetchTableCellDataForIndexPaths:(NSArray*)indexPaths
{
for (NSIndexPath* indexPath in indexPaths) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[NSThread sleepForTimeInterval:0.2]; // emulation of time-consuming and thread-blocking operation
NSString* value = [NSString stringWithFormat:#"Text at cell #%ld", (long)indexPath.row];
dispatch_async(dispatch_get_main_queue(), ^{
self.tableData[indexPath.row] = value;
[self.tableView reloadRowsAtIndexPaths:#[indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
});
});
}
}
#pragma mark UITableViewDataSource protocol
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.tableData.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == (self.tableData.count - 1)) {
for (NSUInteger i = 0; i < 20; i++) {
[self.tableData addObject:self.tableElementPlaceholder];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"TableViewCell" forIndexPath:indexPath];
NSString* text = [self.tableData objectAtIndex:indexPath.row];
if (text.length == 0) {
cell.activityIndicator.hidden = NO;
[cell.activityIndicator startAnimating];
cell.label.hidden = YES;
[self fetchTableCellDataForIndexPath:indexPath];
} else {
[cell.activityIndicator stopAnimating];
cell.activityIndicator.hidden = YES;
cell.label.hidden = NO;
cell.label.text = text;
}
return cell;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
#end
For your project, the performActualFetchTableCellDataForIndexPaths: method would look as follows:
- (void)performActualFetchTableCellDataForIndexPaths:(NSArray*)indexPaths
{
for (NSIndexPath* indexPath in indexPaths) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSData* data = [DKStoreManager loadFileWithName:[finalArray objectAtIndex:indexPath.row] forFolderNumber:[self.FolderCode intValue] forUser:[self.PassedUserID intValue] andType:self.FolderType];
NSDictionary *myDictionary = (NSDictionary*) [NSKeyedUnarchiver unarchiveObjectWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
self.tableData[indexPath.row] = myDictionary;
[self.tableView reloadRowsAtIndexPaths:#[indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
});
});
}
}
Please note that you'll need to use atomic properties self.FolderCode and self.PassedUserID instead of instance variables _FolderCode and _PassedUserID, because loading file is performed in a separate thread and you need to make this data thread-safe.
As for the tableElementPlaceholder method, it might look as follows:
- (id)tableElementPlaceholder
{
return [NSNull null];
}
Correspondingly, in the tableView: cellForRowAtIndexPath: method check if data load completed would look like this:
NSObject* data = [self.tableData objectAtIndex:indexPath.row];
if (data == [NSNull null]) {
cell.activityIndicator.hidden = NO;
[cell.activityIndicator startAnimating];
cell.label.hidden = YES;
[self fetchTableCellDataForIndexPath:indexPath];
} else if ([data isKindOfClass:[NSData class]]) {
[cell.activityIndicator stopAnimating];
cell.activityIndicator.hidden = YES;
NSData* actualData = (NSData*)data;
// Initialize your cell with actualData...
}
Not a good idea
Loading a lot of cells on a tableView it's not a good way to display data. Let suppose if a user wants to take a look at your data, he will take a look at your first elements (suppose max 20 cells), why he should drag to see elements all the way down? If he wants more he just goes down and clicks load more. User doesn't want to keep waiting and loading all the data to the memory (RAM).
Suggestion
For me the best way is to add a load more function (also called infinite scrolling) when you go to the bottom of your loaded elements (it's the same way as pagination on websites).
Implementation
There is a great library to achieve this thing called SVPullToRefresh. This library allow you by draging down the tableView to trigger a method (completionHandler) where you should load more data at the end of tableView.
//This is where we add infinite scrolling to the tableView
__weak YourViewController *weakSelf = self;
[self.tableView addInfiniteScrollingWithActionHandler:^{
[weakSelf loadMoreData];
[weakSelf.tableView reloadData];
}];
Now that we added load more, we need to setup our data. First we declare a NSInteger *lastIndex; as an instance variable, an inthe viewWillAppear we instantiate it with 0, and call loadMoreData which will give the lastIndex the value 20
- (void)viewWillAppear:(BOOL)animated {
lastIndex = 0;
[self loadMoreData];
}
This is the method that takes care of data from Array to tableView. Here we are describing an example which gets 20 elements for load more pressed.
-(void)loadMoreData {
//Lets suppose we are loading 20 elemets each time
//Here we control if we have elements on array
if(yourDataArray.count - lastIndex>0) {
//We control if we have 20 or more so we could load all 20 elements
if(yourDataArray.count - lastIndex>=20) {
lastIndex += 20;
} else {
//Our last index is the last element on array
lastIndex = yourDataArray.count - lastIndex;
}
} else {
//We have loaded all the elements on the array so alert the user
}
}
In numberOfRowsInSection we return the lastIndex
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return lastIndex;
}
Conclusions
To deal with images you should use SDWebImage as the other answers suggested. Dealing the problem of scrolling this way, you eleminate the way to add large amount of data on tableViews at the first look at it. Maybe it's not the right answer to deal with your idea (if you really need to load all data to your tableView), but it tells you how to avoid loading data at giving you performance at scrolling to your tableView.
p.s I am using this example in my Social Networking App and it's working great. (Also Instagram, Facebook and other apps are dealing with loading data this way).
Hope it helps :)
I have seen similar approaches as yours in the past years and they all have been wrong. If I were you I would use 2 different approaches:
1) If you load images from a remote server use a great library called SDWebImage or write your one of own. It's going to get as simple as just [self.imgViewBack setImageWithURL:[NSURL URLWithString:data.eventImageURL];
2) You could also save all your images to NSDocumentDirectory and while them stored there - just use an NSDictionary of paths.
My general recommendation here goes as follows - never create an array or dictionary of raw data of objects the size more than a few kilobytes. This will result in terrible freezes and memory waste all over the place. Always use paths to the object and store them somewhere else - and you will benefit.
Generally cellForRowAtIndexPath method is the most frequently used in UITableViewDataSource protocol so it should be executed with lightning fast speed.
If you have to store everything locally and not on a server as SergiusGee said, I would suggest keeping, for example, only 50 images in memory.
There's the tableView:willDisplayCell:forRowAtIndexPath: delegate method, where you could control which elements are loaded.
For example, when displaying the 50th cell, you have images between index 30-70. After every 10th cell, you could unload the unneeded ones and reload new ones to keep a [x-20, x+30] range, where x is the current cell.
Obviously this will take quite some effort to implement, but loading everything and keeping it in memory is certainly not the answer.
Also, the numbers are just for the examples sake.
Do not load the data from file in -cellForRowAtIndexPath:.
Try loading them into an array of UIImage in -viewDidLoad: synchronously or async-ly (which is up to you) because it does not take too much time for UIImage to be presented but it is extremely expensive to load raw data to construct an UIImage.

cellForRowAtIndexPath not being called after successful api call

In my app(Using Storyboards FYI) I have a viewController which contains a tableView. My viewController hits an API and returns an array of items that my tableView should populate with. The problem is, after successfully retrieving the array, the reloadData method does not trigger a call to cellForRowAtIndexPath. I've set the delegate and datasource properly, and still have no luck. Here is my code:
in my viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView = [[UITableView alloc]init];
self.tableView.dataSource = self;
self.tableView.delegate = self;
[[CCHTTPClient sharedClient] getItemsWithSuccess:^(NSArray *items) {
self.items = items;
[self.tableView reloadData];
} failure:^(NSError *error) {
}];
}
and also in my numberOfRowsInSection:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.items count];
}
I've also found that if i did an API call on the previous VC and set the array of the viewController's items before the viewController is pushed, the table populates properly. really unsure about what's going on here. Any help would be gratefully appreciated !
self.tableView should be an outlet if your controller and table view are made in a storyboard. You're creating a different table view in viewDidLoad with alloc init, so you're calling reloadData on that one, not the one you have on screen. Remove that alloc init line, and connect the outlet in IB.
I would suspect this is caused by calling reloadData from a secondary thread. Try this:
[[AGHTTPClient sharedClient] getItemsWithSuccess:^(NSArray *items) {
self.items = items;
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
} failure:nil];

Refresh tableview not working

I am new in objective-c developpment, I need to find a way to refresh my tableview.
I have 2 UIViewControllers, in the second one I insert data into my database and then I instantiate the first viewcontroller, it contains my tableview. I call a method that allows it to recover all of the data from the database, but when I use [tableview reloadData] nothing happens and cellforrowatindexpath isn't called.
econdviewcontroller:
//I insert data in database and I instanciate class where my tableview is and call refresh method
first = [[FirstviewController alloc]initWithNibName:#"FirstviewController" bundle:nil];
[first refreshList];
in Firstviewcontroller
-(void)refreshList{
self.tableview= [[[UITableView alloc] initWithFrame:self.view.bounds] autorelease];
tableview.dataSource = self;
tableview.delegate = self;
NSMutableArray *array = [[NSMutableArray alloc] init];
//I recover my data from data base
IPADAGRIONEActivityList *arrayActivities = [IPADAGRIONEActivity findAll];
if ([arrayActivities length] > 0)
{
for (IPADAGRIONEActivity * oneRec in arrayActivities)
{
[array addObject:oneRec];
}
}
//activities is NSMutablearray that contains all my data
self.activities = array;
//I build dictionnary
[self buildObjectsDictionnary:activities
NSLog(#"self.act%#",self.tableview);
[array release];
[self.tableview reloadData];
}
//numberofrowsinSection:
NSLog(#"rows%d",[[objects objectForKey:[objectsIndex objectAtIndex:section]] count]);
return [[objects objectForKey:[objectsIndex objectAtIndex:section]] ;
//numberOfSection:
NSLog(#"nbre of section%d",[objectsIndex count]);
return [objectsIndex count];}
//CellforRowatInddexPath: It dosen't access to this method
if (cell== nil) {
cell = [[MHCActivityListCell alloc]init];
}
IPADAGRIONEActivity *activite;
cell.activityCategory.text = [NSString stringWithFormat:#"%#", [activite EMAIL]];
You are instantiating a new tableview every time you call -refreshList. Remember that the tableView is a view, thus it needs to be added as a subview.
Once you understand that, you'll see that there is no reason to alloc+init a new instance of tableView every time you need it to refresh (and if, for any reason you need it, you need to add the tableView as a subview again). Basically, you need to make sure that the tableView that is being displayed is the same tableView that you're storing on your tableView property.
As mentioned earlier by Bruno, you are instantiating your tableview every time and this is not the right approach. You need to refresh the same tableview which had the original data.
in your refresh list do something like this
-(void) refreshList
{
// Get a reference to your viewcontroller's tableview
UITableview *tableview = [self tableview];
// Do your data manipulation
[tableview reloadData];
}

Resources