PDA

View Full Version : reloadData on refresh only




puc
Jul 17, 2008, 10:41 AM
I have built a view based application which currently has a single view and a single window. When the app is launched, the view is displayed instantly. On the view I have a UISearchBar and UITableView both added using Interface Builder.

When the app is launched, the UITableView calls the delegate methods I have created to get the initial data, which is nothing. I have defined:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
which returns 1 when first called (no data...returning 0 generates an exception)

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
which returns 0 when first called (no data)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
which sets the cell text to be blank

When the search button is pressed, another method is called which runs through and populates an array with data. After completing

[tableView reloadData];

is called which should cause the 3 functions above to be executed again, therefore getting the new data which has been populated into the array.

I have verified using NSLog that those 3 functions are called at the beginning of the app launch and when the search button is pressed, the data is populated into the array.

However, when reloadData is called, the 3 functions are not called. They are only called when, in the simulator, the table is scrolled back and forth e.g. row 0 updates after it has disappeared and reappeared.

I need the whole table to update immediately where I'm calling reloadData. Any idea why this isn't happening?



Enuratique
Jul 17, 2008, 07:52 PM
Interestingly enough, I have a program that does the exact same thing. Here's how mine is architected (it worked on the first try):

I have a ViewController for my View that has a search bar and table view on it. This ViewController implements the UISearchBarDelegate and UITableViewDelegates (the only reason I implement UITableViewDelegate is so that the willSelectRowAtIndexPath method returns nil - preventing selection which is what I want from my app, your case may be different). Essentially, this ViewController is responsible for handling the events of my UI elements on this view. In addition, I wrote a custom data source object which I have a reference to in my ViewController.

-MyViewController.h

@class MyDataSource;

@interface MyViewController : UIViewController <UISearchBarDelegate, UITableViewDelegate> {
IBOutlet UISearchBar *searchBar;
IBOutlet UITableView *matchesTable;
MyDataSource *dataSource;
}

@property (nonatomic, retain) UISearchBar *searchBar;
@property (nonatomic, retain) UITableView *matchesTable;


-MyDataSource.h

@class SearchEngine;

@interface MyDataSource : NSObject <UITableViewDataSource> {
SearchEngine *engine;
NSArray *results;
}


For the purposes of discussion, the SearchEngine class essentially has a static method which takes in a NSString and queries a sql_lite database and returns an NSArray of matches (also NSStrings). The data source object has an instance method on it called update which basically refreshes its local results NSArray with the search results sorted.


- (void)update:(NSString *)pattern {
NSArray *searchResults;

[results release];
searchResults = [engine searchList:pattern];
results = [[searchResults sortedArrayUsingFunction:customSortFunction context:nil] retain];
}


These are my data source object's UITableViewDataSource protocol implementations:


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [results count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
NSString *word;

cell = [tableView dequeueReusableCellWithIdentifier:@"MyIdentifier"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"MyIdentifier"] autorelease];
}

// retrieve word at this indexPath's section/row and set cell text apropriately
word = [results objectAtIndex:indexPath.row];

cell.text = word;
return cell;
}


Finally, back in the ViewController, I have the following when the textDidEndEditing:


- (void)searchBarCancelButtonClicked:(UISearchBar *)sender {
searchButtonClicked = NO;
[sender resignFirstResponder];
}

- (void)searchBarSearchButtonClicked:(UISearchBar *)sender {
searchButtonClicked = YES;
[sender resignFirstResponder];
}

- (void)searchBarTextDidEndEditing:(UISearchBar *)sender {
NSIndexPath *indexPath;

if (searchButtonClicked) {
// perform search by updating data source and refreshing table
[dataSource update:sender.text];
[matchesTable reloadData];

// and scroll the view back up to the top
indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[matchesTable scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
}
}


The key here is setting the UITableView's datasource property in your ViewController's viewDidLoad method which was initialized in your ViewController's awakeFromNib method:


- (void)awakeFromNib {
dataSource = [[MyDataSource alloc] init];
}

- (void)viewDidLoad {
[super viewDidLoad];

searchBar.autocapitalizationType = UITextAutocapitalizationTypeAllCharacters;
searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
searchBar.showsCancelButton = YES;

matchesTable.dataSource = dataSource;
}


Hope all that helps!

puc
Jul 18, 2008, 02:56 AM
Thanks for the detailed reply.

How are you binding the UI elements from Interface Builder? If I bind the UITableView (delegate and dataSoruce) and UISearchBar (delegate) to File's Owner, when the app launches, it crashes in viewDidLoad:

tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0x450b40
2008-07-18 08:50:01.074 AmaPhone[5917:20b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[AmaPhoneViewController tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0x450b40'

If I unbind the UITableView dataSource from within IB then it fixes the crash, but the data source methods are never called.

puc
Jul 18, 2008, 06:28 AM
Figured it out - just needed to bind the delegates and then File's Owner to the IBOutlets. Thanks!

Enuratique
Jul 19, 2008, 08:20 AM
Figured it out - just needed to bind the delegates and then File's Owner to the IBOutlets. Thanks!

Yeah, I had trouble adding my data source object to my ViewController's xib file in Interface Builder and associating it to my UITableView's datasource outlet. So I just decided to do it in the code instead (just for the data source) and it worked. Perhaps someone out there can enlighten us how to do that? I think it may even be a bug in Interface Builder.