My test application is used to parse data from remote xml file. It includes a UITabBarController with 4 other controllers such as 2 UINavigationControllers and 2 UIViewControllers.
I use my AppDelegate to get connection and then send NSData to NSOperation file called ParseOperation. After parsing all data, it will return all data to AppDelegate again using NSNotification. It's my code function.
// Our NSNotification callback from the running NSOperation to add the albums
- (void)addAlbums:(NSNotification *)notif {
assert([NSThread isMainThread]);
[self addAlbumsToList:[[notif userInfo] valueForKey:kAlbumResultsKey]];
}
- (void)addAlbumsToList:(NSArray *)albums {
// insert the albums into our ViewController's data source (for KVO purposes)
[self.albumViewController insertAlbums:albums];
NSLog(#"Count: %d", [albumViewController.albumList count]); ==> HERE return value 0
}
And in my AlbumViewController (extends from UITableViewController) has this function
#pragma mark - KVO support
- (void)insertAlbums:(NSArray *)albums
{
[self willChangeValueForKey:#"albumList"];
[self.albumList addObjectsFromArray:albums];
[self didChangeValueForKey:#"albumList"];
}
I call NSLog to test [self.albumViewController insertAlbums:albums] ==> There is no data or value here but albums argument variable has data -_- !!! And when run application, and see nothing because
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [albumList count];
}
==> It returns 0
So I dont known what happen with my app and with function
- (void)addAlbumsToList:(NSArray *)albums {
// insert the earthquakes into our rootViewController's data source (for KVO purposes)
[self.albumViewController insertAlbums:albums];
NSLog(#"Count: %d", [albumViewController.albumList count]); ==> HERE return value 0
}
Related
I have RSS, and the UITableView loads data from it, how can I show UIAlertView if the UITableView is empty, but not by count of an array because the array is different every time it is refreshed, is there some kind of function which will check if UITableView is empty after it completes drawing?
You can query the UITableView itself. So assuming you have a single section you could do the following ...
// Reload the tableview
[tableView reloadData];
// Test the number of rows in the first section
if ([tableView numberofRowsInSection:0] == 0) {
// Display UIAlertView here
}
EDIT; Based on the comments below ...
In your header file (.h) iVar declarations ...
int feedCount;
int feedsParsed;
In your implementation ...
- (void)refresh {
feedCount = 0;
feedsParsed = 0;
[feedParser stopParsing];
self.title = #"Refreshing...";
[parsedItems removeAllObjects];
for (NSString *imePredmeta in [Data variables].mojiPredmeti) {
... // I've removed these lines for brevity but they are still required
[feedParser parse];
feedCount += 1;
}
// Delete everything else after this line
}
- (void)feedParserDidFinish:(MWFeedParser *)parser {
feedsParsed += 1;
if (feedsParsed == feedCount) {
[self.tableView reloadData];
if ([self.tableView numberofRowsInSection:0] == 0) {
// Fade out tableview and display alert here as we now know for
// sure that there are no more feeds to parse and we definitely
// have nothing to display
}
}
}
You will need to loop round the array and check to determine if there are no results.
Please post your existing tableview code if you want a more explicit answer.
edit: micpringle answer above is better.
I am currently using the ASIHTTPRequest wrapper to connect to my database though a php script, basicly it queries the database then returns the result set in an xml format.
from there i instantiate my parser method which inturn passes all the requiered info to the NSXMLParserDelegate methods
//..
parser:didStartElement:namespaceURI:qualifiedName:attributes:
//..
parser:foundCharacters:
//..
parser:didEndElement:namespaceURI:qualifiedName:
//..
The data is first of all passed through a NSMutableData variable when the initial steam of data comes down.
I then have an if statment in my parser:didStartElement:namespaceURI:qualifiedName:attributes: that passes all the data into the string on the basis of the condition
if ([elementName isEqual:#"item"]) {
// NSLog(#"Found title!");
itemString = [[NSMutableString alloc] init];
}
from here I am wondering how I set the row count of the tableview inside the numberOfSectionsInTableView method? what variable would I call count on or would I have to create something else?
There is great tutorial for parsing XML in Apple Sample Source Code
It's called SeismicXML
http://developer.apple.com/library/ios/#samplecode/SeismicXML/Listings/Classes_RootViewController_m.html#//apple_ref/doc/uid/DTS40007323-Classes_RootViewController_m-DontLinkElementID_8
in your question you have the error: numberOfSectionsInTableView didn't return the row count - it's return sections count. In most casesif you have ordinary tableview it should return 1.
and this code return number of rows in this section
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [YOUR_ARRAY count];
}
If I am not misunderstanding you question following will help.
1 Hope you are already saving all values of items in some array in elementDidEnd method.
2 Write some code like following.
if([parser parse]){
[tableView reloadData];
}
This will reload table once parsing is done.
3 Return count of array in which you stored all the item values and fill the table with that array.
Please post if you need any more help.
Hello I am using dropbox api and displaying meta data from dropbox account..
I want to differentiate files and folders from loaded data..because I want to show next level if there is folder and if there is file I don't want to show next View
my code to load data
- (void)restClient:(DBRestClient*)client loadedMetadata:(DBMetadata*)metadata {
[self.metaArray release];
self.metaArray = [[NSMutableArray alloc]init ];
for (DBMetadata *child in metadata.contents) {
NSString *folderName = [[child.path pathComponents] lastObject];
[self.metaArray addObject:folderName];
}
[self.tableView reloadData];
[self.activityIndicator stopAnimating];
}
According to the Dropbox Developer Docs the metadata includes a property called is_dir which should allow you to determine whether the particular item is a directory or not.
Looking at the header of DBMetaData it is indeed exposed as a property
#property (nonatomic, readonly) BOOL isDirectory;
So you can just do a simple test like so
- (void)restClient:(DBRestClient*)client loadedMetadata:(DBMetadata*)metadata
{
if (metadata.isDirectory) {
// handle directory here
} else {
// handle file here
}
}
With regards pushing views based on whether or not an entry is a directory, you could subclass UITableViewCell and add an isDirectory property. Instead of adding just the name to self.metaArray you could add a dictionary containing both the name and the value of isDirectory. Then in your table view datasource where you populate the cells you'd set the isDirectory property of the UITableViewCell based on the same property in the appropriate dictionary from the array. Finally, in the table view delegate method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
you can get the selected cell using the indexPath and then test the isDirectory property and based on it's value take the appropriate action.
Hope this helps.
Using the API V2 of Dropbox with the Dropbox SDK is:
DropboxClient *client = [DropboxClientsManager authorizedClient];
[[client.filesRoutes listFolder:path]
response:^(DBFILESListFolderResult *result, DBFILESListFolderError *routeError, DBRequestError *error) {
if (result) {
for (DBFILESMetadata *entry in result.entries) {
if ([entry isKindOfClass:[DBFILESFileMetadata class]]) {
DBFILESFileMetadata *fileMetadata = (DBFILESFileMetadata *)entry;
NSLog(#"File: %#", fileMetadata.name);
} else if ([entry isKindOfClass:[DBFILESFolderMetadata class]]) {
DBFILESFolderMetadata *folderMetadata = (DBFILESFolderMetadata *)entry;
NSLog(#"Folder: %#", folderMetadata.name);
}
}
}
I'm trying to speed up my application performance by performing calculations in background threads but I'm having trouble doing this. Originally I had been using
[self performSelectorInBackground:#selector(calculateValue:) withObject:[words objectAtIndex:row]];
which was fine when my selector was a void method. However, I'm trying to do something similar to but obviously the below code isn't valid.
int value = [self performSelectorInBackground:#selector(calculateValue:) withObject:[words objectAtIndex:row]];
Any help is greatly appreciated.
Updated
Here is the route I'm currently going. I don't know how to call back to the main thread to send the updated value from computeWordValue to my cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
int value = [self performSelectorInBackground:#selector(calculateWordValue:) withObject:[wordsSection objectAtIndex:row]];
NSString *pointValue = [[NSString alloc] initWithFormat:#"Point:%d",value];
cell.pointLabel.text = pointValue;
}
-(void)calculateWordValue:(NSString *)word {
[self performSelectorOnMainThread:#selector(computeWordValue:) withObject:word waitUntilDone:YES];
}
-(int)computeWordValue:(NSString *)word {
return totalValue; //This will be a randomly generated number
}
Heres a way I use to do it:
-(void) calculateValue:(id) obj
{
// calculate value
[self performSelectorOnMainThread:#selector(didFinishCalculating:) withObject:[NSNumber numberWithInt:value]];
}
-(void) didFinishCalculating:(NSNumber *) val
{
// do what you need to do here
}
This really doesn't solve your problem I don't think, but it should at least give you a starting point.
UPDATE:
Your new code shows me that you don't really need to perform this in the background, just cache the value using an NSDictionary or something. Here's an example:
-(int) calculateValue:(id) obj
{
if ([valuesCache objectForKey:obj] == nil)
{
// calculate value
[valuesCache setObject:[NSNumber numberWithInt:result] forKey:obj];
return result;
}
else
{
return [[valuesCache objectForKey:obj] intValue];
}
}
There's no way -performSelectorInBackground: ... could return the value of the method you're calling because it actually returns before the selector was even executed. That selector will be executed on a background thread asap.
The solution is handling the result of your method asynchronously, as Richard pointed out (the method in his answer should be - (void)didFinishCalculating:(NSNumber*)val, because only objects can be passed in -performSelector: ... calls):
Perform your selector on a background thread
Call your result handler method on the main thread. You should do that on the main thread in just about any case because some things in Mac OS X and iOS are designed to just run on the main thread, like GUI updates.
I've my own class which should do the request and the data processing (parsing). This class should be used from different view controllers. In this class I have implemented:
- (void)sendRequest:(NSString *)url;
- (void)requestFinished:(ASIHTTPRequest *)request;
- (void)requestFailed:(ASIHTTPRequest *)request;
- (id)parse:(NSString *)aString;
I have created a property called result. If the request arrives, requestFinished is called. In requestFinished the results are saved in result. I thought if I return a value in sendRequest I get the result back. But as I mentioned before requestFinished gets the result and so sendRequest is always returning a nil variable, because at that time the request isn't finished.
What can I do to return a result? I want that this class can be used from different view controllers. So my first thought creating a method in my view controller and passing the result won't work.
I read this thread Pass Result of ASIHTTPRequest "requestFinished" Back to Originating Method about using the view controller as delegate. But then I think I have to implement requestFinished and requestFailed in the view controller. The idea of not having duplicate code in different view controllers would be gone away ...
Can someone help?
So finally I did it with a delegate.
In my abstraction class (*.h) I defined the following, otherwise you get
warning: no '-setDarkness: ' method found
warning: (Messages without a matching method signature will be assumed
warning: to return 'id' and accept '.. .' as arguments.)
#protocol RssParserDelegate <NSObject>
- (void)displayResults:(NSDictionary *)parserResults;
#end
Than I declared an instance variable and some methods in my abstraction class (*.h):
id _delegate;
// ...
- (id)delegate;
- (void)setDelegate:(id)new_delegate;
The declaration therefore looks like (*.m):
- (id)delegate {
return _delegate;
}
- (void)setDelegate:(id)new_delegate {
_delegate = new_delegate;
}
To pass the result you have to put the following code on requestFailed and requestFinished:
if ([_delegate respondsToSelector:#selector(displayResults:)])
[_delegate displayResults:results];
else
{
[NSException raise:NSInternalInconsistencyException
format:#"Delegate doesn't respond to displayResults:"];
}
I redefined sendRequest and set the incoming delegate to my instance variable:
- (void)sendRequest:(NSString *)url withDelegate:(id)aDelegate {
// set delegate to populate the results later
[self setDelegate:aDelegate];
In your view controller declare this method:
- (void)displayResults:(NSDictionary *)results {
// do some data processing
...
// show data
[self.tableView reloadData];
}
And I also changed the method call to sendRequest of my abstraction class:
[yourRequest sendRequest:#"xxx" withDelegate:self];