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.
Related
I am using the code below to grab a farme from a video to use as a thumb nail. This is working as desired, however my MBProgressHUD is only showing up after the image grab has been completed, flashing on and off the screen in a second. I have used MBPRogressHUD several times in the same way, displaying it at the start of the code and hiding after everything else is done, which has always worked but this time it is like the code is running out of order? Any help will be much appreciated.
This is the method called on my button press.
- (IBAction)grabImage:(id)sender {
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeIndeterminate;
hud.labelText = #"Grabing Thumbnail";
[hud show:YES];
self.imageholder.image = [self grabImageMethod];
[hud hide:YES];
}
And this is the grab method called with in.
-(UIImage*)grabImageMethod{
NSURL *vidURL = [NSURL URLWithString:#"http://myserver.com/myfile.mp4"];
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:vidURL options:nil];
AVAssetImageGenerator *generate = [[AVAssetImageGenerator alloc] initWithAsset:asset];
NSError *err = NULL;
CMTime time = CMTimeMake(1, 60);
CGImageRef imgRef = [generate copyCGImageAtTime:time actualTime:NULL error:&err];
NSLog(#"err==%#, imageRef==%#", err, imgRef);
return [[UIImage alloc] initWithCGImage:imgRef];
}
After the comment from nielsbot i went and replaced
[hud show:YES];
self.imageholder.image = [self grabImageMethod];
[hud hide:YES];
with
[hud showAnimated:YES whileExecutingBlock:^{
self.imageholder.image = [self grabImageMethod];
}];
It now works perfectly.
I am trying to load images by their URL and store them in NSMutableArray in order. My current code works properly if I were not to care about storing the images in order, however it stores them not in order. It currently stores the images in the articleImage array based on the speed at which the asynchronous requests are completed. I have tried playing around with insertObject:AtIndex but could not get anything to work. To clarify, the NSMutableArray that I am trying to store the images in (in orderly fashion) is articleImage.
Here is some code from my viewDidLoad:
dispatch_async(dispatch_get_main_queue(), ^{
if(articleInfoJSONArray.count > 0)
{
for(int i=0; i<articleInfoJSONArray.count; i++)
{
[issueID addObject:[[articleInfoJSONArray objectAtIndex:i] objectForKey:#"issueID"]];
[articleID addObject:[[articleInfoJSONArray objectAtIndex:i] objectForKey:#"articleID"]];
NSString *imageLink = [[articleInfoJSONArray objectAtIndex:i] objectForKey:#"articleImage"];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageLink]];
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
[articleImage addObject:image];
if(articleImage.count == articleInfoJSONArray.count)
[self imagesLoaded];
});
});
}
}
});
Here is my imagesLoaded:
- (void)imagesLoaded
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
ViewController * vc = [storyboard instantiateViewControllerWithIdentifier:#"MainView"];
[self presentViewController:vc animated:NO completion:nil];
}
Try to use dispatch_group. A dispatch group monitors work that has been added to it, and it will know when that work is done. :) http://commandshift.co.uk/blog/2014/03/19/using-dispatch-groups-to-wait-for-multiple-web-services/
One way i did an image download is with NSOperationQueue and NSOperation. You could define a NSOperationQueue in your header file:
#property (strong, nonatomic) NSOperationQueue *sequentialOperationQueue;
in your implementation do:
self.sequentialOperationQueue = [[NSOperationQueue alloc] init];
self.sequentialOperationQueue.maxConcurrentOperationCount = 1;
then you can add:
for (NSDictionary *imageDict in imagesToFetch) {
ImageDownloadOperation *imgDownloadOperation = [[ImageDownloadOperation alloc] initWithImageLocationDict:imageDict];
[self.sequentialOperationQueue addOperation:imgDownloadOperation];
}
LogoDownloadOperation is a subclass of NSOperation. this way you always have only one active download and process them in the order you want. For details on NSOperation check the apple doc.
in extract i did in ImageDownloadOperation:
- (void)start {
NSURL *imageUrl = [NSURL URLWithString:self.imageDict[#"imageUrl"]];
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
NSURLSessionDownloadTask *downloadPhotoTask = [session
downloadTaskWithURL:imageUrl
completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
if (error) {
self.sessionTask = nil;
[self done];
return;
}
NSData *imageData = [NSData dataWithContentsOfURL:location];
NSBlockOperation *saveImageBlockOperation = [NSBlockOperation blockOperationWithBlock:^{
[SharedAppDelegate.entityManager saveImage:imageData
imageDict:self.imageDict
inManagedObjectContext:SharedAppDelegate.managedObjectContext];
}];
saveImageBlockOperation.qualityOfService = NSQualityOfServiceBackground;
[[NSOperationQueue mainQueue] addOperation:saveImageBlockOperation];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
self.sessionTask = nil;
[self done];
}];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}
As you can see, i store the imageData via my AppDelegate in CoreData. Instead of my way, you could give the ImageDownloadOperation a pointer to your NSMutableArray, then you can store the data direct in your array.
You could make an array of [UIImage new] then once the task is complete
replace the empty image images[i] = newImage
EDIT
NSMutableArray *imageArray = [NSMutableArray new];
for (int i=0; i<articleInfoJSONArray.count; i++) {
[imageArray addObject:[UIImage new]];
}
for (int i=0; i<articleInfoJSONArray.count; i++) {
dispatch_async(dispatch_get_main_queue(), ^{
//download image
imageArray[i] = downloadedImage;
});
}
Hi in my application i have the image on UIImageView and i have button called download once user click the download button the image will save to the Photolibrary. Now i want to add the Progressbar for the download to know the user its downloading the image.
I have used the MBProgressHUD for the progress but not able work like i wanted. I want to show once user click the download button like its downloading message with progress once image got downloaded i want to show like image downloaded please tell me how to achieve this one i tired something like that its not working.
My MBProgressHUD code.
- (IBAction)down:(id)sender {
HUD = [[MBProgressHUD alloc] initWithView:self.view];
HUD.labelText = #"downloading...";
HUD.mode = MBProgressHUDModeAnnularDeterminate;
[self.view addSubview:HUD];
[HUD showWhileExecuting:#selector(download) onTarget:self withObject:nil animated:YES];
UIImageWriteToSavedPhotosAlbum(imageview.image, nil, nil, nil);
}
- (void)download {
float progress = 0.0;
while (progress < 1.0) {
progress += 0.01;
HUD.progress = progress;
usleep(50000);
}
}
I have used the above code to achieve but its not working like i wanted please tell me how to achieve this one.
Thanks.
- (IBAction)down:(id)sender {
HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.mode =MBProgressHUDModeAnnularDeterminate;
NSString *strloadingText = [NSString stringWithFormat:#"Downloading...."];
HUD.labelText = strloadingText;
[HUD show:YES];
[HUD showWhileExecuting:#selector(doSomeFunkyStuff) onTarget:self withObject:nil animated:YES];
}
- (void)doSomeFunkyStuff {
float progress = 0.0;
while (progress < 1.0) {
progress += 0.01;
HUD.progress = progress;
usleep(5000000);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
^{
// process in background thread
NSString *strloadingText = [NSString stringWithFormat:#"completed"];
HUD.labelText = strloadingText;UIImageWriteToSavedPhotosAlbum(imageview.image, nil, nil, nil);
[HUD hide:YES];
});
}
}
UIImageWriteToSavedPhotosAlbum works on main thread. So, put this function in GCD. Your UI can not update itself when main thread is busy.
Try this:
- (IBAction)down:(id)sender {
...
[HUD showWhileExecuting:#selector(download) onTarget:self withObject:nil animated:YES];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
^{
// process in background thread
UIImageWriteToSavedPhotosAlbum(imageview.image, nil, nil, nil);
// when finish, dismiss HUD
HUD.hidden = YES;
});
}
you can use https://github.com/samvermette/SVProgressHUD for showing progress bar..If you are downloading image from server then use async call. Like if you are using ASIFormDataRequest request for getting image then use as follow :
[SVProgressHUD showWithStatus:#"Please wait"];
self.view.userInteractionEnabled = NO;
NSURL *url=[NSURL URLWithString:#"your URL request"];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setRequestMethod:#"POST"];
[request setPostValue:xyz forKey:#"abc"];
[request.shouldAttemptPersistentConnection = NO;
request.delegate=self;
request.timeOutSeconds = 10;
[request startAsynchronous];
After this you can call :
-(void)requestFinished:(ASIHTTPRequest *)request
{
NSError *error = [request error];
if (!error)
{
NSString *response = [request responseString];
NSMutableDictionary *responseJSON = [response JSONValue];
[SVProgressHUD dismiss];
self.view.userInteractionEnabled = YES;
UIImageWriteToSavedPhotosAlbum(imageview.image, nil, nil, nil);
}
}
Hope this will help you.
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.
I am using MBProgressHUD to show an activity indicator while downloading web service data. The app will be often be used in rural areas with poor connectivity so I want to be able to set a timeout for the HUD to say, 10 seconds (as just an arbitrary figure). I'm not sure how to go about this. Can anyone offer suggestions? THanks!
MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:self.view];
NSString *info = [NSString stringWithFormat:#"Loading %# gauges", self.stateIdentifier];
[hud setLabelText:info];
[hud setDetailsLabelText:#"Please wait..."];
[hud setDimBackground:YES];
[hud setOpacity:0.5f];
[hud show:YES];
[self.view addSubview:hud];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
stateGauges = [[GaugeList alloc] initWithStateIdentifier:stateIdentifier andType:nil];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
[hud removeFromSuperview];
});
});
You can use the:
- (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay;
method of MBProgressHUD.
Create MBProgressHUD like:
MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:self.view];
[self.view addSubview:hud];
NSString *info = [NSString stringWithFormat:#"Loading %# gauges", self.stateIdentifier];
[hud setLabelText:info];
[hud setDetailsLabelText:#"Please wait..."];
[hud setDimBackground:YES];
[hud setOpacity:0.5f];
[hud show:YES];
[hud hide:YES afterDelay:10.0];
For Swift, following is the solution:
let progressHUD = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
progressHUD.hideAnimated(true, afterDelay: 5)