I'm a new programmer in Objective-C and I have a terrible problem in my first application.
I have one class called Places (NSObject) where I found places in a Google places and return an NSArray.
I have another classe called DisplayPlaces (UiViewController). This class imports my "Place.h".
In my viewDidLoad I have this code:
Places *places = [[Places alloc]init];
[places.locationManager startUpdatingLocation];
[places LoadPlaces:places.locationManager.location];
[places.locationManager stopUpdatingLocation];
[places release];
In my method LoadPlaces I load JSON URL and put a result in NSDictionary after I get only places and put in NSArray and return.
Into my Places I alloc my DisplayPlaces and call a method ReturnPlaces: NSArray to return places that I found.
- (void)ReturnPlaces:(NSArray *)locais{
placesArray = locais;
[self.UITableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [placesArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIndentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIndentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIndentifier] autorelease];
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.text = [placesArray objectAtIndex:indexPath.row];
return cell;
}
It all works.
My problem is:
In my ReturnPlaces: NSArray I call [MyUiTableView reloadData] but I can't refresh my UiTableView.
What can I do?
Set your tableview yourTableView as your property and use
self.yourTableView = tableView; // for assigning the tableView contents to your property
if you are reloading inside any method for tableView, just use
[tableView reloadData];
if you are reloading outside your tableView methods, use
[self.yourTableView reloadData];
Are you calling reloadData on the instance of your tableView. In other words if you have
MyUITableView *mytableView;
then reloading it would require you call
[mytableView reloadData];
not
[MyUITableView reloadData];
Related
I a UITableView called tableView. It's data array called namesArray.
I have a function that adds a name to the array that looks like this:
-(void)addName:(NSString*)name
{
[self.namesArray addObject: name];
[self.tableView reloadData];
}
After I call reloadData on tableView, the last cell (the one that was added) is not showing on tableView, numberOfRowsInSection return the actual number so there is a space for another cell but there is not an actual cell.
I was debugging cellForRowAtIndexPath and I was found out that when cellForRowAtIndexPath called for the new cell, dequeueReusableCellWithIdentifier returns nil while when it called to the other cells (except when indexPath.row == 0 of course) it returns a cell.
The code for cellForRowAtIndexPath:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier=#"Cell";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textLabel.text=[self.namesArray objectAtIndex:indexPath.row];
return cell;
}
numberOfRows:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.namesArray.count;
}
Note: if I try to print the last object of namesArray using NSLog it's looking fine (the last object is the new one that was created) so it's a problem with reloading the data of tableView
Can you please help me? Thanks!
check the number of rows you returns in numberOfRowsInSection
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
it should be something like:
[self.namesArray count];
First dataSource and delegate the tableview. after that make sure using below method with perfection
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [your array count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier=#"Cell";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textLabel.text=[self.namesArray objectAtIndex:indexPath.row];
return cell;
}
After that reload tableView with
[_tableViewName reloadData];
-(void)addName:(NSString*)name
{
[self.namesArray addObject: name];
[self.tableView reloadData];
}
If you are calling this function again and again to fill the array then, do not reload tableview in this method. Because, its getting reloaded overtime you call this method and cellForRowAtIndexPath gets called every time. So, after your array gets filled completely then, reload the tableview
.h
#interface ViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>
{
IBOutlet UITextField *txtName;
IBOutlet UITableView *tableObject;
NSMutableArray *namesArray;
}
- (IBAction)btnAdd:(UIButton *)sender;
.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
namesArray = [[NSMutableArray alloc] init];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
{
return namesArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
static NSString *cellIdentifier=#"Cell";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textLabel.text=[namesArray objectAtIndex:indexPath.row];
return cell;
}
-(void)addName:(NSString*)name
{
[namesArray addObject: name];
[tableObject reloadData];
}
- (IBAction)btnAdd:(UIButton *)sender {
[self addName:txtName.text];
}
I'm new in CoreData and using MagicalRecord to rule with it. My problem is that I have the UITableView with an NSArray as dataSource populated with objects which fetched from CoreData db, and everything seems fine until I scroll the table for some times.
Here is my code:
Method for fetching data (MyDatabase.m):
+(NSArray *)getEntities{
...
return [MyEntity MR_findAllSortedBy:#"name" ascending:YES withPredicate:predicate];
}
Here is how I fetch and set data to UITableView in my ViewController:
- (void)viewDidLoad {
[super viewDidLoad];
myEntitiesArray = [MyDatabase getEntities];
if(myEntitiesArray.count != 0)
[myTableView setTableData:myEntitiesArray];
}
Here is setTableData method implementation in MyTableView.m:
- (void)setTableData:(NSArray *)array {
if (array && [array count] > 0) {
_tableData = array;
[self reloadData];
}
}
And here is how I set up my cells in MyTableView.m:
- (void)tableView:(UITableView *)tableView willDisplayCell:(SSCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
cell.nameLabel.text = [(MyEntity *)_tableData[indexPath.row] name];
}
I tried to put an NSLog(#"name is %#",[(MyEntity *)_tableData[indexPath.row] name]) into willDisplayCell and found that when cells become empty, NSLog prints out the messages "name is (null)". I know this question is possibly solved by many people and many times before I faced this problem. Hope someone will help me to solve it too :)
UPDATE: cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"ssCell";
SSCell *cell = (SSCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if( !cell ) {
[self registerNib:[UINib nibWithNibName:#"SSCell" bundle:nil] forCellReuseIdentifier:cellIdentifier];
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
[cell setSelectedBackgroundView:selectedBackgroundView];
}
cell.nameLabel.text = [(MyEntity *)_tableData[indexPath.row] name];
return cell;
}
I also call this method inside MyTableView.m init method:
[self registerNib:[UINib nibWithNibName:#"SSCell" bundle:nil] forCellReuseIdentifier:#"ssCell"];
You have to use cellForRowAtIndexPath. In this method the cells are allocated.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
/*
* This is an important bit, it asks the table view if it has any available cells
* already created which it is not using (if they are offscreen), so that it can
* reuse them (saving the time of alloc/init/load from xib a new cell ).
* The identifier is there to differentiate between different types of cells
* (you can display different types of cells in the same table view)
*/
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyIdentifier"];
/*
* If the cell is nil it means no cell was available for reuse and that we should
* create a new one.
*/
if (cell == nil) {
/*
* Actually create a new cell (with an identifier so that it can be dequeued).
*/
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"MyIdentifier"];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
/*
* Now that we have a cell we can configure it to display the data corresponding to
* this row/section
*/
cell.nameLabel.text = [(MyEntity *)_tableData[indexPath.row] name];
return cell;
}
You should be initializing the cell by calling init. Instead you are doing the following:
if( !cell ) {
[self registerNib:[UINib nibWithNibName:#"SSCell" bundle:nil] forCellReuseIdentifier:cellIdentifier];
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
[cell setSelectedBackgroundView:selectedBackgroundView];
The second call attempts to again reuse an existing cell when there isn't one available. That would probably return nil again.
be very careful of the "feature" of objective C, where calling a method of a nil object does nothing. Instead of crashing with null.pointer.exception like Java, it probably floats over [cell setSelectedBackgroundView:selectedBackgroundView] and a whole bunch of other lines without a problem.
I have a viewcontroller set up on my storyboard, and I have inserted a tableView inside this view controller. I want to do a [self.tableView reloadData]; on viewDidLoad, but it says the property tableView isn't found.
Here is what the code for my tableView in the viewcontroller.m file looks like:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return #"Shared with";
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return activeUsers.count;
}
- (sharedUsersTable *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"sharedUser";
sharedUsersTable *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[sharedUsersTable alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *key = [activeUsers objectAtIndex:indexPath.row];
NSString *name = [key objectForKey:#"shared_user_name"];
NSString *email = [key objectForKey:#"email"];
cell.textLabel.text = [NSString stringWithFormat:#"%#", name];
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#", email];
return cell;
}
Am I missing some special command to call a tableView inside a view controller which isn't a TableViewCOntroller?
You need to make a IBOutlet of tableView and call reloadData on that.
This depends on how you declared your UITableView.
If it is a property (#property (nonatomic, strong) UITableView *myTableView), it will be [self.myTableView reloadData]. If you declared it as just an instance variable (UITableView *_myTableView), it will be [_myTableView reloadData].
you'll have to define tableview datasource and delegate as self.. like table.datasource=self,
and table.delegate=self.....you can do it in viewdidload....but if you have downloaded the data somewhere, then you can do it in that place where you have downloaded and reload the table there. please assign the delegate and datasource to the table and reload the tableview after you have downloaded the data....and please make a property like(#property (nonatomic, strong) UITableView *table)and use self when you do the reload...
I was trying to update tableview in masterviewcontroller from detailviewcontroller using delegate. I called reloadData from delegate methods but it didn't wwork. I still couldn't solve this.
this is my delegate method in MasterViewController
- (void)updateScore:(DetailViewController *)controller withScore:(NSUInteger)score {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:_selectedIndexPath];
NSLog(#"%#", cell.detailTextLabel.text);
cell.detailTextLabel.text = [NSString stringWithFormat:#"Best score: %lu", (unsigned long)score];
[self.tableView reloadData];
NSLog(#"%#", cell.detailTextLabel.text);
}
from NSLog the cell.detailTextLabel.text was updated, but the tableview doesn't reload
thanks
You need to make sure that your view controller is the tableview delegate and the datasource
If you are using storyboards, under connections inspector, your tableview needs your view controller set as dataSource and delegate
if you want to just do it in your viewcontroller.m file in the viewDidLoad method you can use these lines
self.tableView.delegate = self;
self.tableView.dataSource = self;
- (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];
//other init
}
if(_selectedIndexPath.row == indexPath.row && _selectedIndexPath.section == indexPath.section){
cell.detailTextLabel.text = [NSString stringWithFormat:#"Best score: %lu", (unsigned long)score];
}
}
Your can try move code in cellForRowAtIndexPath above then tableView reloadData
[self.tableView reloadData] updates all visible cells in the table. It calls numberOfSectionsInTableView, numberOfRowsInSection, cellForRowAtIndexPath and so on. In other words: It completely renews the table.
The only correct way to set cell content is to set it in cellForRowAtIndexPath. The code:
if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; //other init}
never gets called since iOS 6 if you use storyboards.
So with your code you:
use an incorrect way to setup cell content.
clear all your settings with [self.tableView reloadData].
Solution:
Save score in __strong ivar or property.
Call [self.tableView reloadData]. It calls cellForRowAtIndexPath at appropriate times.
Setup a new score in cellForRowAtIndexPath method.
Suggestion: Use:
dispatch_async(dispatch_get_main_queue(), ^(){ [self.tableView reloadData]; });
to return fast from the delegate and not wait until the table was updated.
I am trying to get the 'Search Bar and Display Controller' functionality to work in an iOS app. I am able to NSLog in repsonse to a search query and hardcode in a new array but am unable to get the table view to repopulate. I have the following for in response to a user submit on the search button:
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
NSLog(#"You clicked the search bar");
NSMutableArray *filteredResults=[[NSMutableArray alloc] init];
Person *filteredPerson=[[Person alloc] init];
filteredPerson.firstName=#"Josie - here i am";
[filteredResults addObject:filteredPerson];
_objects=filteredResults;
self.tableView.dataSource = _objects;
[self.tableView reloadData];
}
Any ideas on making this repopulate would be appreciated.
thx
edit #1
It looks like this is populating the _objects NSMutableArray:
- (void)insertNewObject:(id)sender
{
if (!_objects) {
_objects = [[NSMutableArray alloc] init];
}
[_objects insertObject:[NSDate date] atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
Should I just create a new _objects and use insertNewObject rather than the addObject code I have above? Would this bypass the need to deal with the dataSource property of the table view?
edit 2
per #ian
- (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];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
/*
NSDate *object = [_objects objectAtIndex:indexPath.row];
cell.textLabel.text = [object description];
*/
Person *rowPerson = [_objects objectAtIndex:indexPath.row];
cell.textLabel.text = [rowPerson firstName];
return cell;
}
thx
I have a UITableView that uses an NSMutableArray to hold the data. Here's how it works: set the UISearchDisplayController's delegate to your TableView controller, and when the UITableViewDelegate methods are called (numberOfRows, numberOfSections, cellForRowAtIndexPath, etc) you can do the following to serve up the search data when appropriate:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger numberOfRows = 0;
if (tableView == self.searchDisplayController.searchResultsTableView)
{
//This is your search table --- use the filteredListContent (this is the NSMutableArray)
numberOfRows = [filteredListContent count];
}
else
{
//Serve up data for your regular UITableView here.
}
return numberOfRows;
}
You should take a look at the UISearchDisplayDelegate documentation. You can use these methods to update your filteredListContent array, as follows:
#pragma mark -
#pragma mark Content Filtering
- (void)filterContentForSearchText:(NSString*)searchText
{
//In this method, you'll want to update your filteredListContent array using the string of text that the user has typed in. For example, you could do something like this (it all depends on how you're storing and retrieving the data):
NSPredicate *notePredicate = [NSPredicate predicateWithFormat:#"text contains[cd] %#", searchText];
self.filteredListContent = [[mainDataArray filteredArrayUsingPredicate:notePredicate] mutableCopy];
}
#pragma mark UISearchDisplayController Delegate Methods
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
The line self.tableView.dataSource = _objects; is setting your array as the datasource for the UITableView. I assume that you don't have an NSArray subclass that implements the UITableViewDataSource protocol?
Try removing that line, and letting your existing datasource handler deal with the change in data.