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
Related
In my table's viewForHeaderInSection, I'm setting the variable _searchTerm, using the section index like so:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 21)];
headerView.backgroundColor = [UIColor colorWithRed:0.949 green:0.949 blue:0.949 alpha:1];
_searchTerm = [[[[_matchCenterData objectAtIndex:section] objectForKey:#"Top 3"] objectAtIndex:0]objectForKey:#"Search Term"];
return nil;
}
For each section, I want to use its respective _searchTerm variable in cellForRowAtIndexPath like so:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//load top 3 data
NSDictionary *currentSectionDictionary = _matchCenterData[indexPath.section];
NSArray *top3ArrayForSection = currentSectionDictionary[#"Top 3"];
//... code removed to brevity
// title of the item
cell.textLabel.text = _searchTerm;
}
This doesn't seem to be working, the cells textLabel is showing up as blank. Is it possible to use a variable from viewForHeaderInSection in cellForRowAtIndexPath, and if so, what am I doing wrong?
Not directly answer your question, but you could achieve the result that you want by assigning cell.textLabel.text to your data source.
cell.textLabel.text = [[[[_matchCenterData objectAtIndex:indexPath.section] objectForKey:#"Top 3"] objectAtIndex:0]objectForKey:#"Search Term"];
now, the possible reason that your cell.textLabel.text is blank, is that because cellForRow method is called first before viewForHeaderInSection
First, consider that anything you use in cellForRowAtIndexPath that varies as a function of indexPath as part of your datasource. Second, don't initialize datasource data viewForHeader. Do so in the same place you initialize the rest of the data (the rest of _matchCenterData)
So, if you have:
#property(strong,nonatomic) NSArray *matchCenterData;
Then also have...
#property(strong,nonatomic) NSArray *searchTerms; // notice the plural name
Where you have:
self.matchCenterData = // whatever
Then also have (in pseudo code)...
for (each section in self.matchCenterData) {
[self.searchTerms addObject:the thing you were setting in viewForHeader]
Now, your cellForRowAtIndexPath, can behave properly with respect to this additional datasource data...
cell.textLabel.text = self.searchTerms[indexPath.section];
Incidentally, an better datasource might include the sectional information in the matchCenterData, rather than the slightly hackier "parallel array" idea I've proposed here.
Even more incidentally, notice how I declare a property named "foo", then refer to it as self.foo, not _foo. With just a few exceptions, that's a practice you should adopt, too.
I am populating a tableview from data that is received from a server. The data is a list of user activities within a given timeframe. One such activity is "Login". I do not wish to populate my tableview with this string but I'm not sure how to skip it when populating my tableview.
Here is how I populate the cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{ static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
#try{
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString *action = [object valueForKey:#"theActionName"];
if ([action isEqualtoString:#"Login"]) {
return cell;
}
return cell;
}#catch (NSException *ex) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
return cell;
}
}
As you can see I tried using return cell but as you probably know it gives me a blank cell when the table is displayed. I'm sure there is a simple line of code for this but I came up blank with the search terms I used. Could someone please enlighten me! Thanks!
P.S. you may be thinking I am not putting anything in any of the cells but I pulled out a bunch of code to keep this short.
UPDATE:
Thanks for the heads up on "isEqualtoString:" Everything worked fine with "isEqual" but I changed it given that I received so many suggestions to do so. But this is not what I am asking.
To be more clear if I had an array containing the terms: view, view, login, view. When my tableview was populated I would have 4 cells that said; view, view, login, view. I simply want to ignore the term login so that I would have 3 cells that all said view. Thanks!
There can be many way to do this.
I Belive that UITabelView should display what its datasource (here datasource is self.fetchedResultsController) contains.
What you can do is create another NSArray from self.fetchedResultsController which does not contain this object.
Try this:
NSMutableArray *newSource = [[NSMutableArray alloc] init];
for(int i = 0; i < self.fetchedResultsController.count ; i++)
{
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString *action = [object valueForKey:#"theActionName"];
if (![action isEqual:#"Login"])
{
[newSource addObject:action];
}
}
[tableView reloadData];
Now use newSource instead of self.fetchedResultsController
You might think that using one more array is not good. But believe it it is far easier than using the same array with condition. You don't have to worry about that condition when you perform some operation with your UITableView like remove object by using indexpath.
try using if ([action isEqualToString:#"Login"])
When you want to compare strings you need to use this isEqualToString .
Change this line
if ([action isEqualToString:#"Login"]) {
return cell;
}
You are using the wrong function to compare your input string and the given data variable.
They both are NSString objects so use :
if([action isEqualToString:#"Login"])
{
//enter your code here
}
#Ben : I am assuming that you have registered you cell through nib as you are using dequeueReusableCellWithIdentifier.
Make your tableview content as "Dynamic prototype" (You can see this in Attributes Inspector of table view) and change your table view cell style as custom (You can see this in Attributes Inspector of tableview cell).
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!
I have created a table view controller with a UITextField on it. How can I populate the tableview controller cell of the input text that came from the UITextField. I tried the following but it seems the array is empty or being emptied
- (IBAction)endingInput:(id)sender{
[sender resignFirstResponder];
//send the message to the table cell
NSMutableArray *history = [[NSMutableArray alloc] init];
[history addObject:[NSString stringWithFormat:#"%#",self.chatMessageField.text]];
[self.chatHistory addObject:history];
[myTableView reloadData];
//push the message to server
[self initiateMessageSending:self.chatMessageField.text];
chatMessageField.text = #"";
[history release];
}
On my cellForRowAtIndex method;
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if(tableView == self.myTableView){
cell.textLabel.text = [chatHistory objectAtIndex:indexPath.row];
}
return cell;
I have set a break point but it doesn't pass there. I suspect my array is empty.
Likewise, where is the best place to call the reloadData method?
Also, chatHistory is private member, is there a way to initialize it like [[self.chatHistory alloc] init]; ?
I know its a common question but I've been haggling with this for sometime now.
I think you have forgotten to alloc and init your chatHistory
- (void)viewDidLoad
{
self.chatHistory = [[NSMutableArray alloc] init];
}
If you aren't initializing chatHistory it doesn't even exist...never mind being empty! (I'm not clear what "private member" means here.)
If you log the value of chatHistory, you'll see for sure whether it is nil or empty.
The other problem I see is that you're trying to load chatHistory with other (history) arrays but then use its content as a string. You should decide whether it's going to be an array of arrays or an array of strings and then stay consistent.
(Also [[self.chatHistory alloc] init]; isn't valid syntax for creating a new object. Look at an Objective-C language reference for the right way.)
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.