Refresh tableview not working - ios

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];
}

Related

UICollectionView is not reloading data properly

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();
}

UITableView not reloading correctly

I've been having this problem a lot where the table view either doesn't load the cells the first time or displays temporary labels. In both cases, when the table is loaded a second time (by another button in which I force it to reload or by going to the previous view in the app and going back to the table) everything shows up as it was supposed to.
Im using [table reloadData] to reload the table in the viewDidAppear: method as it didn't work in the viewDidAppear: method and in another case I put it in a button action.
I'm also using this method to set the label of the cell that I have placed in the storyboard:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [contactsTableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
UILabel *label = (UILabel *)[cell viewWithTag:1001];
label.text = [Array3 objectAtIndex:indexPath.row];
return cell;
}
EDIT
I declare the array below the implementation like:
NSMutableArray * Array3;
After that in the ViewDidLoad method I declare the rest of the array:
Array3 = [NSMutableArray arrayWithObjects: nil];
Then I load the elements in the array by calling a function within the ViewDidLoad method to fill the array with elements.
EDIT 2
Showing more of how I populate the array
- (void)viewDidLoad {
[super viewDidLoad];
Array3 = [NSMutableArray arrayWithObjects: nil];
[self loadArray];
}
- (void) loadArray
//populate array in here.
// I use the add object method
// [Array3 addobject:object];
{
Good news first: Your method cellForRowAtIndexPath is correct.
There is no need to invoke reloadData in neither -viewDidAppear: nor -viewWillAppear. This remark may not hold true if you modify the content of Array3 while your view is covered, which is a special case.
It is not a matter of where you define your method, but where you invoke it. A reasonable place to populate the content of Array3 is in
required init(coder aDecoder: NSCoder!)
or
override func viewDidLoad()
Pitfalls
Additionally, you need all your delegate methods (numberOfSectionsInTableView, numberOfRowsInSection, cellForRowAtIndexPath) to be returning consistent values, in accordance to your array. A simple technique is to use the number of entries in the array to drive numberOfRowsInSection.
When to use reloadData?
A typical use of reloadData is shown below:
- (void)loadArray {
//populate array in here.
...
[self.tableview reloadData]; // Possibly postpone to main thread
}
Give the cell identifier unique for example you can
NSString *cellIdentifier =[NSString stringWithFormate:#"%d",indexPath.row];
Maybe this will help you...
I think the Array3 is nil When the first load.
I believe that in your initial set up for Array3 you must allocate this variable to memory to then be used in a different function, like the tableView:cellForIndexPath:
Therefore instead of Array3 = [NSMutableArray arrayWithObjects: nil]; you would use Array3 = [[NSMutableArray alloc] init];
Since you're leaving it as an empty array on this line of code, you would not use [[NSMutableArray alloc] initWithObjects: nil]; because it is literally the same deal

UITableview in UITableviewcontroller - cellforrowatindexpath not called on reload

I am stucked in a stupid problem since two days. I have got a UITableViewController pushed in Navigation Controller. When it loads, since there is no data, so empty table is visible:
But when I receive data from server, and call [self.tableView reloadData], both numberOfRowsInSection and heightForRowAtIndexPath get invoke except cellForRowAtIndexPath and my controller is shown without table:
I can't really understand that why it is happening. All datasource methods are called except for cellForRowAtIndexPath. Please someone guide me... Thanks..
ActLogController.h
#interface ActLogController : UITableViewController<ASIHTTPRequestDelegate,UITableViewDataSource,UITableViewDelegate>
#property(strong) NSMutableArray *activityKeys;
#end
ActLogController.m
- (void)viewDidLoad
{
[super viewDidLoad];
activityKeys = [[NSMutableArray alloc] init];
self.tableView.dataSource = self;
}
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self retrieveActivityLogFromServer];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return activityKeys.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
ActivityLogUnit *act = [activityKeys objectAtIndex:indexPath.row];
cell.textLabel.text = act.price;
return cell;
}
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 50.0;
}
-(void) requestFinished:(ASIHTTPRequest *)request
{
NSArray *list = [request.responseString JSONValue];
for (int i = 0; i < list.count; i++) {
NSArray *singleTrade = [[list objectAtIndex:i] JSONValue];
ActivityLogUnit *unit = [[ActivityLogUnit alloc] init];
unit.symbol = [singleTrade objectAtIndex:0];
unit.type = [singleTrade objectAtIndex:1];
unit.price = [singleTrade objectAtIndex:2];
unit.volTraded = [singleTrade objectAtIndex:3];
unit.remVol = [singleTrade objectAtIndex:4];
unit.actualVol = [singleTrade objectAtIndex:5];
unit.recordID = [singleTrade objectAtIndex:6];
unit.orderNo = [singleTrade objectAtIndex:7];
unit.time = [singleTrade objectAtIndex:8];
[activityKeys addObject:unit];
}
if(activityKeys.count > 0)
{
[self.tableView reloadData];//it is called and I have got 6 items confirm
}
}
EDIT
I set some dummy data in my array activityKeys, Data is being displayed in table, and cellforrowatindexpath is called successfully. But as I reload data after sometime, other methods are called except this one and table disappears as shown in 2nd pic. Any ideas?
Your problem is that you probably download the data content on a background thread. Since you cannot update the UI on a background you need to call [self.tableView reloadData] on the main thread once the download is finished!
Hope it helps!
Looks like you in secondary thread, do reloadData in main thread by using following code
[self.tableView performSelectorOnMainThread#selector(reloadData) withObject:nil waitUntilDone:NO]
You can always use [NSThread isMainThread] to check whether you are in main thread or not.
you have to write in viewdidload
self.tableView.dataSource = self;
self.tableView.delegate = self;
Edit
You have no xib then where you are declared/sets your tableview's properties. Like
self.tableView.frame = CGRectMake(0, 45, 320, 500);
self.tableView.rowHeight = 34.0f;
self.tableView.separatorStyle=UITableViewCellSeparatorStyleNone;
[self.view addSubview:self.tableView];
[self.tableView setBackgroundColor:[UIColor clearColor]];
self.tableView.showsVerticalScrollIndicator=NO;
self.tableView.dataSource = self;
self.tableView.delegate = self;
Try with
#property(nonatomic,strong) NSMutableArray *activityKeys;
Firstly I strongly believe that the instance name of the tableview should not be similar to the local variable (i.e. tableView in class should not be equal to tableView in delegate and data source methods).
Second in your question posted I could not see the delegate set for the table view.
answer Posted By Samir Rathod should work if you have #property for the table view set in you .h or .m file.
You can also do this if you have a XIB file.
Press ctrl and click + drag the tableview to the files owner and set the delegate and datasource.
For me the problem was my stubbed-out code returning 0 as the number of sections (so it never asked how many rows were in the section, and never got their data). Just change that to 1 if it's your problem also. Additionally, I was working in Swift, where the issue mentioned by #shahid-rasheed is coded (slightly) differently:
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
At last I got it worked. cellForRowAtIndexPath was not being called because of a line of code I didn't mention here... which was actually removing some color background layer from view. It was causing reloading issue. After removing it, everything works fine.
Thank you all of you for your cooperation :)
I had the same symptoms too. In my case, the first time I loaded the data (from core data) in viewDidLoad, NSSortDescriptor was used to sort the data.
On the click of a button, the core data was fetched again (this time with changes) and tableView data reloaded. It initially gave me a blank table view after the button was clicked because I forgot to sort the data the second time I fetched it.
Learning points: Remember to call all methods which modify the cell (like background color mentioned by iAnum, or NSSortDescriptor in my case) if you have used them in the beginning!

How to insert Cell in UICollectionVIew Programmatically?

I have a UICollectionView and it works fine, but I want to add a few UICollectionViewCells items programmatically into the collection view.
So how can I achieve this?
To further clarify: when I say programmatically I mean inserting a cell during runtime, when an action is fired, not when the app is loaded (using the viewDidLoad method). I know when the model is updated and the call is made to UICollectionView in the insertItemsAtIndexPaths: method. It should create a new cells, but it's not doing that, it's throwing an error.
...By referring to UICollectionView documentation
You can accomplish:
Inserting, Deleting, and Moving Sections and Items To insert, delete,
or move a single section or item, follow these steps:
Update the data in your data source object.
Call the appropriate method of the collection view to insert or delete the section or item.
It is critical that you update your data source before notifying the
collection view of any changes. The collection view methods assume
that your data source contains the currently correct data. If it does
not, the collection view might receive the wrong set of items from
your data source or ask for items that are not there and crash your
app. When you add, delete, or move a single item programmatically, the
collection view’s methods automatically create animations to reflect
the changes. If you want to animate multiple changes together, though,
you must perform all insert, delete, or move calls inside a block and
pass that block to the performBatchUpdates:completion: method. The
batch update process then animates all of your changes at the same
time and you can freely mix calls to insert, delete, or move items
within the same block.
From your Question: you can for example register A gesture Recognizer, and Insert a NEW cell by
doing the following:
in
// in .h
#property (nonatomic, strong) NSMutableArray *data;
// in .m
#synthesize data
//
- (void)ViewDidLoad{
//....
myCollectonView.dataSource = self;
myCollectionView.delegate = self;
data = [[NSMutableArray alloc] initWithObjects:#"0",#"1", #"2" #"3", #"4",
#"5",#"6", #"7", #"8", #"9",
#"10", #"11", #"12", #"13",
#"14", #"15", nil];
UISwipeGestureRecognizer *swipeDown =
[[UISwipeGestureRecognizer alloc]
initWithTarget:self action:#selector(addNewCell:)];
swipeDown.direction = UISwipeGestureRecognizerDirectionDown;
[self.view addGestureRecognizer:swipeDown];
//..
}
-(void)addNewCell:(UISwipeGestureRecognizer *)downGesture {
NSArray *newData = [[NSArray alloc] initWithObjects:#"otherData", nil];
[self.myCollectionView performBatchUpdates:^{
int resultsSize = [self.data count]; //data is the previous array of data
[self.data addObjectsFromArray:newData];
NSMutableArray *arrayWithIndexPaths = [NSMutableArray array];
for (int i = resultsSize; i < resultsSize + newData.count; i++) {
[arrayWithIndexPaths addObject:[NSIndexPath indexPathForRow:i
inSection:0]];
}
[self.myCollectionView insertItemsAtIndexPaths:arrayWithIndexPaths];
} completion:nil];
}
If you are inserting multiple items into UICollectionView, you can use performBatchUpdates:
[self.collectionView performBatchUpdates:^{
// Insert the cut/copy items into data source as well as collection view
for (id item in self.selectedItems) {
// update your data source array
[self.images insertObject:item atIndex:indexPath.row];
[self.collectionView insertItemsAtIndexPaths:
[NSArray arrayWithObject:indexPath]];
}
} completion:nil];
– insertItemsAtIndexPaths: does the job
Here is how to insert an item in Swift 3 :
let indexPath = IndexPath(row:index, section: 0) //at some index
self.collectionView.insertItems(at: [indexPath])
You must first update your data.

how to update data in tableView?

I initialize data in my table with an array in viewDidLoad and then add the data to the cell. This a standard way of doing it that I read in a book.
This is what I have:
- (void)viewDidLoad {
[super viewDidLoad];
//Create array and add data ("tableViewValues" is initialized in .h file)
tableViewValues = [[NSMutableArray alloc]init];
[tableViewValues addObject:#"$280,000.00"];
[tableViewValues addObject:#"$279,318.79"];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSString *cellValue = [tableViewValues objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
So when the view loads, those two currency values are in my table.
Now in another function, I populate a another array with different currency numbers depending on what the user enter in a textfield. How would I update my current table view and replace those values with the values in my other array? Can anyone help me? Thanks!
You can call
[self.tableView reloadData];
to reload all data, however, you will need to program a way to have the array that you want populate the table. Maybe you want your -cellForRowAtIndexPath to call a private method that conditionally picks the correct array.
You have to remove all values from your array then you have to call table reload data
// In the method where you will get new values
[tableViewValues removeAllObjects];
[tableViewValues add:#"new values"];
//reload table view with new values
[self.tableView reloadData];
I've had this problem many times and I always make the same mistake.
[self._tableview reloadData] works!
The question is the place where you populate the table.
I did it in -(void)viewDidLoad and the table was updated with the same data. Apparently nothing happens.
Update the content of your table view (plist, dictionary, array, whatever) in the same place where you call [self._tableview reloadData], just before.
THAT IS THE KEY!!!
Do a test:
#pragma mark - Actions
- (IBAction)refreshParams:(id)sender {
self.dictionary = nil;
[self._tableView reloadData];
}
You can see how disappear the tableview content when you push the refresh button. Obviously, this is an example. I use a dictionary to populate the table and a button to refresh the content.
You'd need to (release and) recreate same tableViewValues array and then call reloadData method on tableView like this:
[self.tableView reloadData];
This will work if you're on table view controller and self.tableView points to the table in question.
in that function, after assigning values to the new array, all you have to do is
[tableViewValues removeAllObjects];
tableViewValues = [[NSMutableArray alloc] arrayByAddingObjectsFromArray:newArray];
[self.tableView reloadData];
[self.tableView reloadData];
will help you for this.
In Swift,
tableView.reloadData() or self.tableView.reloadData(), where tableView is a property in your ViewController.

Resources