dispatch_queue_t and TableView - ios

I'm a noobie in the Objective-C language, and I have a little problem.
In fact, I have 2 TableViews, and when I go from one to the other I parse some XML from the internet. The parsing is doing well, but I wanted to add an UIActivityIndicatorView between those 2 views to tell to the user that something is loading.
So, to do that, I wanted to do the parsing in another thread and show the UIActivityIndicatorView in the main thread. So here's my code :
- (void)viewDidLoad
{
[super viewDidLoad];
UIActivityIndicatorView *activityIndicator;
activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityIndicator.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
activityIndicator.center = self.view.center;
[self.view addSubview: activityIndicator];
activityIndicator.startAnimating;
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_async(queue, ^{
NSError *error = nil;
// we will put parsed data in an a array
titles = [[NSMutableArray alloc] init];
urls = [[NSMutableArray alloc] init];
CXMLDocument *rssParser = [[CXMLDocument alloc] initWithContentsOfURL:[NSURL URLWithString:_emissionSelectionnee] options:0 error:&error];
NSArray *nodes = NULL;
nodes = [rssParser nodesForXPath:#"//rss/channel/item/title" error:nil];
for (CXMLElement *title in nodes) {
[titles addObject:[title stringValue]];
}
nodes = NULL;
nodes = [rssParser nodesForXPath:#"//rss/channel/item/enclosure" error:nil];
for (CXMLElement *url in nodes) {
[urls addObject:[[url attributeForName:#"url"] stringValue]];
}
dispatch_sync(dispatch_get_main_queue(), ^{
activityIndicator.stopAnimating;
});
}
}
So now, the UIActivityIndicator shows up, but the cells are empty.. When I do not use the dispatch_queue_t, it works well..
Does someone have an idea?
Thank you in advance!

You need to reload your Table view (in the same block where you hide the activity indicator):
[self.tableView reloadData]

Related

Using of activity indicator in second viewController to load lot of data in Objective C

How can I use activity indicator in second view controller to let data to be complete load
first VC
- (IBAction)btnOpenHTML:(id)sender {
RecievVC *obj = [self.storyboard instantiateViewControllerWithIdentifier:#"RecievVC"];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:lblName.text forKey:#"lblname"];
//........Lots of Data........(then in the end of 1st VC)........//
obj.dicData = dict;
[self.navigationController pushViewController:obj animated:true];
}
#end
the 2nd VC Code after viewdidload
- (void)viewDidLoad {
[super viewDidLoad];
[self->webView.scrollView setScrollEnabled:true];
[self->webView loadHTMLString:[self getHTMLStirng] baseURL:[NSURL URLWithString:#""]];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
-(NSString *)getHTMLStirng {
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"resumeT" ofType:#"html"];
NSString *strHTML = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
if (self.dicData) {
strHTML = [strHTML stringByReplacingOccurrencesOfString:#"#NAME" withString:[_dicData valueForKey:#"lblname"]];
//...............Lots of Data......Then at the end of 2nd VC............//
}return strHTML;
}
what I main by lots of data, it's around 150 of keys and value have to be send from 1st VC to 2nd VC.
any idea?
You could try GCD:
// instantiate the activity indicator
UIActivityIndicatorView *activity = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleGray];
activity.center = self.view.center;
// start your activity indicator
[activity startAnimating]
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// load data on background thread
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:lblName.text forKey:#"lblname"];
//........Lots of Data........(then in the end of 1st VC)........//
// update UI on main thread
dispatch_async(dispatch_get_main_queue(), ^{
// stop your activity indicator
[activity stopAnimating]
RecievVC *obj = [self.storyboard instantiateViewControllerWithIdentifier:#"RecievVC"];
obj.dicData = dict;
[self.navigationController pushViewController:obj animated:true];
});
});

JSON Data Are Not Parsed For All View

i am newBie in iOS Development.i make a Segmented Control For my Application and I want to Parsed Different WebService Data For Each Scrollview Contain For that i Use HMSegmented Controll and Set A scrollview For it like as
Here my Main view Controller Contain Code liken as
ViewController *latest=[[ViewController alloc]initWithNibName:#"ViewController" bundle:nil];
[latest.view setFrame:CGRectMake(0, 0, self.view.frame.size.width,self.view.frame.size.height)];
latest.index=0;
[self addChildViewController:latest];
[self.scrollView addSubview:latest.view];
ViewController *latest2=[[ViewController alloc]initWithNibName:#"NavuViewController" bundle:nil];
[latest2.view setFrame:CGRectMake(320, 0, self.view.frame.size.width, self.view.frame.size.height)];
latest2.index=1;
[self addChildViewController:latest2];
[self.scrollView addSubview:latest2.view];
ViewController * latest3 =[[ViewController alloc]initWithNibName:#"NavuViewController" bundle:nil];
[latest3.view setFrame:CGRectMake(640, 0, self.view.frame.size.width, self.view.frame.size.height)];
latest3.index=2;
[self addChildViewController: latest3];
[self.scrollView addSubview: latest3.view];
Then it Contain Different View As i want
And in my View Controller.h file i Define index as int Variable
#property (assign) int index;
And Now I want to Fetch Data From Different Webservice Based on Index for that i Write a code in my ViewController.m file ViewDidLoad method i write a Code like as
-(void)view
{
[super viewWillAppear:animated];
[self.navuTable deselectRowAtIndexPath:self.navuTable.indexPathForSelectedRow animated:YES];
NSURL *url;
switch (self.index)
{
case 0:
url=[NSURL URLWithString:[NSString stringWithFormat:#"http://www.janvajevu.com/webservice/latest_post.php?page=%d",pageNum]];
break;
case 1:
{
NSString *urlString = #"http://www.janvajevu.com/webservice/categorylist.php?category=%E0%AA%9C%E0%AA%BE%E0%AA%A3%E0%AA%B5%E0%AA%BE%20%E0%AA%9C%E0%AB%87%E0%AA%B5%E0%AB%81%E0%AA%82&page=";
url = [NSURL URLWithString:[NSString stringWithFormat:#"%#%d",urlString,pageNum]];
}
break;
case 2:
{
NSString *urlString = #"http://www.janvajevu.com/webservice/categorylist.php?category=%E0%AA%9F%E0%AB%87%E0%AA%B2%E0%AB%87%E0%AA%A8%E0%AB%8D%E0%AA%9F&page=";
url= [NSURL URLWithString:[NSString stringWithFormat:#"%#%d",urlString,pageNum]];
}
break;
default:
break;
}
dispatch_async(kBgQueue, ^{
data = [NSData dataWithContentsOfURL: url];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:data waitUntilDone:YES];
});
-(void)fetchedData:(NSData *)responsedata
{
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spinner.frame = CGRectMake(0, 0, 320, 44);
self.navuTable.tableFooterView = spinner;
if (responsedata.length > 0)
{
NSError* error;
self.json= [NSJSONSerialization JSONObjectWithData:responsedata options:kNilOptions error:&error];
if ([[_json objectForKey:#"data"] isKindOfClass:[NSArray class]])
{
NSArray *arr = (NSArray *)[_json objectForKey:#"data"];
[self.navuArray addObjectsFromArray:arr];
[self.navuTable reloadData];
[spinner startAnimating];
NSLog(#"Array %#",self.navuArray);
}
}
Then it is Load Same Data for All Index means Only Fetch Data From First Index Url It is Mot Load Data For Different Index for Different Segmented Control please Give me Solution For it.
thanks in advance.
Index stay at value of 0 all the time, it's not the first item , but it's the default value when you initialize it .
try to add the delegate of the Segment , see documentation and implement the ValueChanged: Delegate in which you can get the value of the selected index by yourSegment.selectedIndex ,, and that will be the value you should use to fetch different URLs.
otherwise the structure of your code is so week , you should use storyboard instead of nibs , maybe make a Model class with clean code then connect to your UI code . (MVC pattern)
hope that helps , post further data if you want more help . Good luck

iOS CollectionView: Loading Data Asynchronously

I'm using a collection view and trying to transition from loading the data synchronously to loading it asynchronously.
I know that the following currently works (it takes a while to load, but all the cells appear correctly when it's done):
// load projectData in main thread
NSData * projectData = [NSData dataWithContentsOfURL:userUrl];
[self performSelectorOnMainThread:#selector(fetchProjects:)withObject:projectData waitUntilDone:YES];
I rewrote it to do everything asynchronously:
// load project data asynchronously
dispatch_async(bgQueue, ^{
UIView *loadingAnimation = loadingCircle;
loadingAnimation.tag = 15;
[self.collectionView addSubview:loadingAnimation];
[loadingCircle startAnimating];
NSData * projectData = [NSData dataWithContentsOfURL:userUrl];
[self performSelector:#selector(fetchProjects:) withObject:projectData];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"finished with loading projects");
UIView *viewToRemove = [self.view viewWithTag:15];
[viewToRemove removeFromSuperview];
[self.collectionView reloadData];
});
});
When I run the app after loading the data asynchronously, the view appears empty (the cells have no content), but when I scroll, some of the cells begin to appear.
Is there anything else I need to call besides reloadData to get my collection cells to appear properly?
Here is my fetchProjects:
// get JSON data of projects
- (void)fetchProjects:(NSData *)responseData {
NSError * error;
NSDictionary * json = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error]; // get dictionary from json data
NSDictionary * data = [json objectForKey:#"data"]; // get data in array
NSArray * projects = [data objectForKey:#"projects"];
NSDictionary * mostRecentProject = [projects objectAtIndex:0];
mostRecentProjectID = [mostRecentProject objectForKey:#"id"];
for (NSDictionary *currentProject in projects)
{
[projectIDs addObject: [currentProject objectForKey:#"id"]];
NSString *projectTitle = [currentProject objectForKey:#"title"];
NSString *trimmedProjectTitle = [projectTitle stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
id delegate = [[UIApplication sharedApplication] delegate];
self.managedObjectContext = [delegate managedObjectContext];
Project *newProject = (Project *) [NSEntityDescription insertNewObjectForEntityForName:#"Project" inManagedObjectContext:[self managedObjectContext]];
CustomLabel *cellLabel=[[CustomLabel alloc]init];
cellLabel.text = trimmedProjectTitle;
NSLog(#"fetchprojects:%#",projectTitle);
[titles addObject:projectTitle];
CGSize maxLabelSize = CGSizeMake(screenWidth/2 - 30,100);
CustomLabel *titleLabel = [[CustomLabel alloc]init];
[titleLabel setNumberOfLines:0];
titleLabel.text = projectTitle;
CGSize expectedLabelSize = [titleLabel.text sizeWithFont:titleLabel.font constrainedToSize:maxLabelSize lineBreakMode:NSLineBreakByWordWrapping];
CGRect labelFrame = (CGRectMake(0, 0, screenWidth/2 - 30, 0));
labelFrame.origin.x = 0;
labelFrame.origin.y = screenWidth/2 - 70 - expectedLabelSize.height;
labelFrame.size.height = expectedLabelSize.height;
titleLabel.frame = labelFrame;
titleLabel.backgroundColor = [[UIColor blackColor]colorWithAlphaComponent:0.5f];
titleLabel.textColor =[UIColor whiteColor];
[titleLabel setFont: [UIFont fontWithName: #"HelveticaNeue" size:12]];
//NSLog(#"%#", titleLabel.text);
UIImageView *imagePreview = [[UIImageView alloc] initWithFrame:CGRectMake(7.5, 10, screenWidth/2 -30, screenWidth/2 -70)];
imagePreview.contentMode= UIViewContentModeScaleAspectFill;
imagePreview.clipsToBounds = YES;
[imagePreview setImage:[UIImage imageNamed:#"blank.png"]];
[imagePreview addSubview:titleLabel];
[imagePreview.subviews[0] setClipsToBounds:YES];
[projectContainers addObject: imagePreview];
}
}
You're doing a lot of UI work on a background thread which you really shouldn't do. From what I can see, the only line that really needs to be run on a background thread is this one:
NSData * projectData = [NSData dataWithContentsOfURL:userUrl];
The rest looks like it deals with setting up and displaying your UI and some CoreData stuff; all of that needs to be run on the main thread. The easiest way to do that and keep everything running in the right order would be something like this:
// NOTE: If you're sure you're already on the main thread here, you don't need the dispatch, but it's not going to hurt to leave it in.
dispatch_async(dispatch_get_main_queue(), ^{
UIView *loadingAnimation = loadingCircle;
loadingAnimation.tag = 15;
[self.collectionView addSubview:loadingAnimation];
[loadingCircle startAnimating];
});
dispatch_async(bgQueue, ^{
NSData * projectData = [NSData dataWithContentsOfURL:userUrl];
dispatch_async(dispatch_get_main_queue(), ^{
[self fetchProjects:projectData];
NSLog(#"finished with loading projects");
UIView *viewToRemove = [self.view viewWithTag:15];
[viewToRemove removeFromSuperview];
[self.collectionView reloadData];
});
});
Note: I also changed [self performSelector:#selector(fetchProjects:) withObject:projectData] to [self fetchProjects:projectData]; you don't really need to go through performSelector: there.

How to add a UIActivityIndicator View in my iOS app?

I don't know how to explain but... If you understand me when I say a "loading circle" perfect, I just want to do this
// Start loading in the middle of the screen frozing all interaction
for (int c = 0; c < ([barcos count] - 1); c++)
{
NSArray *datos = [[barcos objectAtIndex:c] componentsSeparatedByString:#";"];
NSString *nombreImagen = [datos objectAtIndex:2];
NSURL *accesoFtp = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#",urlFtp,nombreImagen]];
NSData *imagen = [NSData dataWithContentsOfURL:accesoFtp];
[imagen writeToFile:[[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent:[NSString stringWithFormat:#"Barcos/%#",nombreImagen]]] atomically:NO];
}
// Stop frozing all interaction and remove the loading circle
Probabbly I have to add a thread or something but I don't know how to do what I want exactly I hope you can help me, again. Thanks.
EDIT:
UIActivityIndicatorView *activityIndicator;
activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityIndicator.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
activityIndicator.center = self.view.center;
[self.view addSubview: activityIndicator];
activityIndicator.startAnimating;
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_async(queue, ^{
for (int c = 0; c < ([barcos count] - 1); c++)
{
NSArray *datos = [[barcos objectAtIndex:c] componentsSeparatedByString:#";"];
NSString *nombreImagen = [datos objectAtIndex:2];
NSURL *accesoFtp = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#",urlFtp,nombreImagen]];
NSData *imagen = [NSData dataWithContentsOfURL:accesoFtp];
[imagen writeToFile:[[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent:[NSString stringWithFormat:#"Barcos/%#",nombreImagen]]] atomically:NO];
}
dispatch_sync(dispatch_get_main_queue(), ^{
activityIndicator.stopAnimating;
});
});
Two things
1.- The activity indicator is too small but works, if I can to it bigger or same size but make darker the background would be better (Thanks!)
2.- I have a warning with startAnimating and stopAnimating "Property access result unused - getters should not be used for side effects"
Thanks =)
It can be done using MBProgressHUD
Also check this code:
//Show your activity indicator here
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_async(queue, ^{
for (int c = 0; c < ([barcos count] - 1); c++)
{
NSArray *datos = [[barcos objectAtIndex:c] componentsSeparatedByString:#";"];
NSString *nombreImagen = [datos objectAtIndex:2];
NSURL *accesoFtp = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#",urlFtp,nombreImagen]];
NSData *imagen = [NSData dataWithContentsOfURL:accesoFtp];
[imagen writeToFile:[[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent:[NSString stringWithFormat:#"Barcos/%#",nombreImagen]]] atomically:NO];
}
dispatch_sync(dispatch_get_main_queue(), ^{
//hide that activity indicator here
});
});
EDIT:
Never call methods like:
activityIndicator.startAnimating;
activityIndicator.stopAnimating;
These are used for calling setters and getters.
Change it to:
[activityIndicator startAnimating];
[activityIndicator stopAnimating];
You can use a UIActivityIndicatorView.
UIActivityIndicatorView *activityIndicator;
activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityIndicator.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
activityIndicator.center = self.view.center;
[self.view addSubview: activityIndicator];
Also, like shown by Midhun MP, you may have to use asynchronism to, for example, load data while your indicator is showing.
May be you can try Activity Indicator.
You can try using svprogresshud
You can alter the style of activityindicator,
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActionSheetStyleDefault];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActionSheetStyleBlackOpaque];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActionSheetStyleBlackTranslucent];
use any of these styles.
And This link can help to solve your second issue
xCode "Property access results unused - getters should not be used for side effects"
SOLUTION:
instead of using activityIndicator.startAnimating; and activityIndicator.stopAnimating
use [activityIndicator startAnimating]; and [activityIndicator stopAnimating];
For changing the size of the activity indicator:
activityIndicator.frame = CGRectMake(0.0, 0.0, 80.0, 80.0);
activityIndicator.transform = CGAffineTransformMakeScale(1.75, 1.75);
By the way, if you are populating a tableview, you may consider enabling the user interaction at the same time you launch the activity indicator:
[activityIndicator startAnimating];
tableView.userInteractionEnabled = NO;
and restoring it after:
[self.tableView reloadData];
[activityIndicator stopAnimating];
tableView.userInteractionEnabled = YES;

issue with using performSelector for activityIndicator while calling web service - IOS

I'm trying to get the performSelector to load the activity indicator on a separate thread while the web service call is made. The issue is the "return parsedData;" is not being set in fetchJSON:. However, when I print the parsedData in getData: method, it's coming back fine. I assume the return is being executed before the performSelector is finished getting the data. Is there any way to have the fetchJSON: method wait for performSelector to finish before returning parsedData?
-(void)showActivityIndicator
{
CGRect frame = CGRectMake(0.0, 0.0, 125.0, 125.0);
loading = [[UIActivityIndicatorView alloc] initWithFrame:frame];
[loading setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhiteLarge];
[loading hidesWhenStopped];
//loading.center=[self tableView].center;
[loading startAnimating];
[loading sizeToFit];
loading.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin);
// initing the bar button
//UIBarButtonItem *loadingView = [[UIBarButtonItem alloc] initWithCustomView:loading];
//loadingView.target = self;
[loadingView addSubview:loading];
}
- (NSDictionary *)fetchJSON:(NSString *)urlString
{
NSMutableString *domain = [[NSMutableString alloc] initWithString:#"http://www.blablabla.com/dev/"];
[domain appendString:urlString];
//NSLog(#"%#", domain);
NSURL *url = [NSURL URLWithString:domain];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
[self showActivityIndicator];
[self performSelector:#selector(getData:) withObject:req afterDelay:0.0];
//[self performSelectorOnMainThread:#selector(getData:) withObject:req waitUntilDone:YES];
return parsedData;
}
-(IBAction)getData:(id)sender
{
NSURLResponse* response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:sender returningResponse:&response error:nil];
parsedData = [NSJSONSerialization
JSONObjectWithData:data
options:NSJSONReadingMutableLeaves
error:nil];
NSLog(#"GET DATA %#", parsedData);
[loading stopAnimating];
loading = nil;
}
"performSelector to load the activity indicator on a separate thread"
??
UIKit is, in general, not thread safe.
Between the commented out code and confusing method declarations (why is getData: an IBAction? Is it called both from within code and as a control's action?), I'm really missing context on what you are trying to do here.
Could you start by just giving a high-level overview of what you're trying to accomplish and how this code fits into that picture?

Resources