Ok here's the question, how do I simulate loading message until I fully downloaded the data from the server. I have this problem as I can't pass the data to the next view controller when the properties to hold the data from the downloaded json is still nil. So, How can I simulate a loading message until I fully parsed the Json.
Here's my code to fetch data
-(void)fetchFeed
{
NSString *requestString = #"some website";
NSURL *url = [NSURL URLWithString:requestString];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary * jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
self.locations = jsonObject[#"someKey"];
NSLog(#"%#", self.locations);
}
];
[dataTask resume];
}
- (void)viewDidLoad
{
[super viewDidLoad];
MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:self.view];
[self.view addSubview:hud];
[hud show:YES];
[hud setLabelText:#"Loading..."];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[self fetchFeed]; //Network activity
dispatch_async(dispatch_get_main_queue(), ^{
//do stuff after json download
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
});
Please check out my answer here.. It pretty much does the same thing that you are looking for..
I have used MBProgressHUD to show the loading message.
Its as simple as
MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:self.view];
[self.view addSubview:hud];
[hud show:YES];
[hud setLabelText:#"Loading..."];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
//Network activity
dispatch_async(dispatch_get_main_queue(), ^{
//do stuff after json download
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
});
for a more detailed answer check the link.
*************EDIT*******************
As you are using NSURLSession it allows you to perform background download operations. As per the code you posted, we don't to start a new thread using dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{})
Please try this..
- (void)viewDidLoad
{
[super viewDidLoad];
[self fetchFeed]; //Network activity
}
-(void)fetchFeed
{
MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:self.view];
[self.view addSubview:hud];
[hud show:YES];
[hud setLabelText:#"Loading..."];
NSString *requestString = #"some website";
NSURL *url = [NSURL URLWithString:requestString];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
[[self.session dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
NSDictionary * jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
self.locations = jsonObject[#"someKey"];
NSLog(#"%#", self.locations);
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
}] resume];
}
This should work.
Related
I want to display the activity indicator while waiting for the API to return. The problem is after all the result I get from API, then the spinner only display. The result I want is while waiting for API call, then the spinner will running.
I'm calling this method in here
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
[self startLoadingSpinner]
//Calling API...
[self stopLoadingSpinner]
}
Here is the method for the activity indicator
-(void)startLoadingSpinner {
self.activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 70, 70)];
self.activityIndicator.opaque = YES;
self.activityIndicator.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.4f];
self.activityIndicator.center = self.view.center;
self.activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
[self.activityIndicator setColor:[UIColor whiteColor]];
[self.view addSubview:self.activityIndicator];
[self.activityIndicator startAnimating];
}
This is how I stop the activity indicator
-(void)stopLoadingSpinner {
[self.activityIndicator performSelector:#selector(removeFromSuperview) withObject:nil afterDelay:0.5];
}
Don't add activity indicators in tableview datasource method - numberOfRowsInSection .
Add these two functions calling in the same method where you are making an API call. Make an API call in ViewDidLoad, some life cycle method or in action methods.
Below is the example of using it.
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:#"http://httpbin.org/get"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
[self startLoadingSpinner]
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(#"Error: %#", error);
} else {
NSLog(#"%# %#", response, responseObject);
}
[self stopLoadingSpinner]
}];
[dataTask resume];
In Swift
func makeAPIRequest(to endPoint: String) {
// here you can showActivetyIndicator start progressing here
self.startLoadingSpinner()
Alamofire.request(endPoint).responseJSON{ response in
if let value = response.result.value {
let responseInJSON = JSON(value)
self._responseInJSON = responseInJSON
}
// here you can hide Your ActivetyIndicator here
self.stopLoadingSpinner()
}
}
My detailed answer is below
-(void)simpleGetResponse{
#try {
//Call the Activity Indicator show method here
[self startLoadingSpinner];
NSString *strURL = #"Your URL";
NSURL *urlStr = [NSURL URLWithString:strURL];
NSMutableURLRequest *mutaURL = [NSMutableURLRequest requestWithURL:urlStr];
[mutaURL setHTTPMethod:#"GET"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:mutaURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if(httpResponse.statusCode == 200)
{
NSError *parseError = nil;
id response = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if(response != nil){
if([response isKindOfClass:[NSDictionary class]]){
NSLog(#"response is in dictionary format %#",response);
NSDictionary *dictRes = [response copy];
NSLog(#"The dictRes is - %#",dictRes);
}
else{
NSLog(#"response is in array format %#",response);
NSDictionary *arrRes = [response copy];
NSLog(#"The arrRes is - %#",arrRes);
}
dispatch_async(dispatch_get_main_queue(), ^{
//Call the Activity Indicator hidden method inside the dispatch_main_queue method
[self stopLoadingSpinner]
[yourTableView reloadData];
});
}
}
else
{
NSLog(#"Error");
}
}];
[dataTask resume];
}
#catch (NSException *exception) {
NSLog(#"%#", [exception description]);
}
#finally {
}
}
I`m trying to down keyboard before do a NSURLResquest and loading show...
[self.txtComentario resignFirstResponder];
crashes de app...I have already tried to resignFirstResponder inside loadingThread() too
-(void) loadingThread {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
[self.myLoadingView setFrame:CGRectMake(0, 0, 320, 568)];
[self.myLoadingImagem setFrame:CGRectMake(133, 250, 54, 9)];
[appDelegate.window addSubview:self.myLoadingView];
[appDelegate.window addSubview:self.myLoadingImagem];
[self.myLoadingView setHidden:NO];
[self animar];
}
-(IBAction)btnComentarClick:(id)sender {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
[self.txtComentario resignFirstResponder];
[NSThread detachNewThreadSelector:#selector(loadingThread) toTarget:self withObject:nil];
NSString* comentarios = [kBaseURL stringByAppendingPathComponent:kComentarios];
NSURL* url = [NSURL URLWithString:comentarios]; //1
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = #"POST"; //2
// NSDictionary *usuarioDict = [[NSDictionary alloc] initWithObjectsAndKeys:txtEmailCadastro.text, #"email", txtSenhaCadastro.text, #"senha", categorias, #"categorias", nil];
NSMutableDictionary* jsonable = [NSMutableDictionary dictionary];
safeSet(jsonable, #"idUsuario", appDelegate.usuarioLogado.identificador);
safeSet(jsonable, #"nomeUsuario", appDelegate.usuarioLogado.nome);
safeSet(jsonable, #"textoComentario", self.txtComentario.text);
safeSet(jsonable, #"idOcorrencia", _location._id);
NSData* data = [NSJSONSerialization dataWithJSONObject:jsonable options:0 error:NULL]; //3
request.HTTPBody = data;
[request addValue:#"application/json" forHTTPHeaderField:#"Content-Type"]; //4
NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession* session = [NSURLSession sessionWithConfiguration:config];
NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { //5
if (!error) {
// NSArray* responseArray = #[[NSJSONSerialization JSONObjectWithData:data options:0 error:NULL]];
NSLog(#"comentado");
dispatch_async(dispatch_get_main_queue(), ^(void){
self.txtComentario.text = #"";
[self.txtComentario resignFirstResponder];
});
}
}];
[dataTask resume];
[self listarComentarios];
}
EDIT: if I try to resignFirstesponder before NSThread, nothing happens, no crash but not keyboard Down..If i try inside loadingThread...the app crashes
the error inside NSThread is:
[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] may only be called
from the main thread.'
It happens sometimes that asynchronous API requests won't call back to the delegate on the main thread. So, just try to ensure that you are on Main thread. If you aren't on it then try to switch to the main thread before you make any update to the UI.
if ([NSThread isMainThread]) {
NSLog(#"Yes, it is!");
} else {
NSLog(#"No, it's not. Please switch to main");
}
try to replace with this code
[self.view endEditing: YES];
I am trying to use MBProgressHUD with NSURLConnection, but I'm not sure how to make the animation spin in MBProgressHUDModeDeterminate.
Basically I have this code in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
[self fetchFromServer];
self.HUD = [ [MBProgressHUD alloc] initWithView:self.view];
self.HUD.labelText = #"Loading Schedule...";
[self.view addSubview:self.HUD];
self.HUD.dimBackground = YES;
[self.HUD show:YES];
}
When this view is loaded, I call a method to fetch from the database. I want MBProgressHUD to load. Then I want it to end once the data has been fetched.
- (void)fetchFromServer
{
NSURL *url = [[NSURL alloc] initWithString:#"url.com"];
[NSURLConnection sendAsynchronousRequest:[ [NSURLRequest alloc] initWithURL:url] queue:[ [NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSString* str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSArray *jsonObj = [NSJSONSerialization JSONObjectWithData:[str dataUsingEncoding:NSUTF8StringEncoding]
options:0 error:&error];
scheduleJSONArray = jsonObj;
// Progress in HUD should end here after scheduleJSONArray is set
[self.HUD hide:YES];
} ];
}
I don't know what to use to update the progress of HUD. Can anyone assist?
Thanks
You better use AFNetworking:
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
// Set up HUD
__weak ViewController *weakSelf = self;
NSURLRequest *request = [[AFHTTPRequestSerializer serializer] requestWithMethod:#"GET" URLString:#"url.com" parameters:nil error:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
__weak AFHTTPRequestOperation *weakOperation = operation;
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
[weakSelf updateHUDForOpetation:weakOperation
totalBytes:totalBytesExpectedToRead
readBytes:totalBytesRead
index:[videoNames indexOfObject:videoName]
total:videoNames.count];
}];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[MBProgressHUD hideHUDForView:self.view animated:YES];
}];
Then update HUD manually. Like this:
- (void)updateHUDTotalBytes:(long long)totalBytes
readBytes:(long long)readBytes {
MBProgressHUD *HUD = [MBProgressHUD HUDForView:self.view];
HUD.mode = MBProgressHUDModeDeterminate;
HUD.labelText = [NSString stringWithFormat:#"%lld/%lld", readBytes / 1024, totalBytes / 1024];
HUD.progress = (double)readBytes / (double)totalBytes;
}
I need to load some images from a JSON Object. When these images are loading, I need to show a HUD on my view. I tried to define a HUD like following,
Define HUD in viewDidLoad method
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeIndeterminate;
hud.labelText = #"Loading Images";
[hud show:YES];
[hud showWhileExecuting:#selector(loadJSONData) onTarget:self withObject:nil animated:YES];
loadJSONData method
-(void)loadJSONData{
_myObject = [[NSMutableArray alloc] init];
NSData *jsonSource = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
id jsonObjects = [NSJSONSerialization JSONObjectWithData:
jsonSource options:NSJSONReadingMutableContainers error:nil];
NSArray *dataDic = [jsonObjects objectForKey:#"data"];
for (NSDictionary *dicData in dataDic) {
Lawyer *l = [[Lawyer alloc] init];
dispatch_async(queue, ^{
NSString *imgUrl = [NSString stringWithFormat:#"myurl",l.imageUrl];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imgUrl]];
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
l.uiImage = image;
});
});
[_myObject addObject:[l initFromDictionary:dicData]];
}
}
My out put is showing the HUD for a second and disappeared. No data is loading. How do I fix this.
Thanks in Advance!
Your code is working as expected. loadJSONData performs an asynchronous request so the selector has actually finished executing. What you should do is show the HUD before the method loadJSONData and then hide it when the data has been received in:
dispatch_async(dispatch_get_main_queue(), ^{
l.uiImage = image;
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
Although personally I would have a callback in loadJSONData and perform the HUD dismissal in a completion block.
Use this code :
MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView: self.view];
[self.view addSubview: hud];
hud.labelText = #"Loading Images...";
[hud showAnimated:YES whileExecutingBlock:^{
[self loadJSONData];
} completionBlock:^{
// Refresh UI
}];
But make sure , as whileExecutingBlock using thread you have to make all UI change on main thread.
I've built a Rails backend for an iOS app that lets users access a RESTful API only after having authorized the device. Basically authorization is managed by retrieving a token.
When the user submits username and password, the webservice gets called with an AFHTTPRequestOperation (see code below). I also display to the user a HUD (MBProgressHUD) to track the progress of the request. I've set up callbacks for success and failure and I want to update the HUD and let it stick onscreen with an updated message for a couple of seconds before dismissing it.
//Set HUD
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeIndeterminate;
hud.labelText = #"Authenticating";
//Set HTTP Client and request
NSURL *url = [NSURL URLWithString:#"http://localhost:3000"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
[httpClient setParameterEncoding:AFFormURLParameterEncoding]; //setting x-www-form-urlencoded
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST" path:#"/api/v1/tokens.json" parameters:#{#"password":_passwordField.text, #"email":_emailField.text}];
//Set operation
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
//Success and failure blocks
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){
NSError *error;
NSDictionary* jsonFromData = (NSDictionary*)[NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:&error];
NSLog(#"%#", jsonFromData);
_statusLabel.text = #"Device authenticated!";
hud.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"37x-Checkmark.png"]];
hud.mode = MBProgressHUDModeCustomView;
hud.labelText = #"Authenticated!";
sleep(2);
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error){
NSLog(#"Error");
sleep(2);
_statusLabel.text = #"Wrong username or password!";
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
}];
When the success / failure operation callbacks get called:
I try to update the HUD mode and text;
I wait sleep() for a couple of seconds;
I dismiss the HUD [MBProgressHUD hideAllHUDsForView:self.view animated:YES];;
I've also tried using dispatch_queue_t dispatch_get_main_queue(void); and run the HUD update on the main thread but to no avail.
Any ideas on what am I getting wrong?