I have a simple script that displays in a UIImageView from the internet.
Is there a way to display a UIActivityIndicatorView or something of that sort to show the user that it's loading?
Here's my minimal code:
NSString *imagePath = [NSString stringWithFormat:#"urlofimagehere.jpg"];
NSData *mydata = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imagePath]];
UIImage *myimage = [[UIImage alloc] initWithData:mydata];
[imageView setImage:myimage];
[myimage release];
Thanks!
Coulton
EDIT: CODE REMOVED
-[NSData initWithContentsOfURL:]
is a synchronous method. This means it will block until the data is loaded over the network. If you want to be able to keep your UI responsive, you'll want to use an asynchronous approach. For more info, read this: http://akosma.com/2010/05/28/initwithcontentsofurl-methods-considered-harmful/
I'd recommend using NSURLRequest with an NSURLRequestDelegate to receive callbacks when the data is finished loading. Here's an example:
- (void)loadImageAtURL(NSString *)urlString {
[myUIActivityIndicatorView startAnimating];
// SHOW NETWORK INDICATOR
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
// SET UP THE REQUEST
NSURL *url = [[NSURL alloc] initWithString:urlString];
NSURLRequest *request = [[[NSURLRequest alloc] initWithURL:url] autorelease];
// SET UP THE CONNECTION
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
mutData = [[[NSMutableData alloc] init] retain];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error {
// Handle error
}
- (void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data {
[mutData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection {
// ONCE LOADED HIDE NETWORK INDICATOR
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
// RELEASE THE CONNECTION
[connection release];
// CREATE NEW UIIMAGE FROM THE DATA
image = [[[UIImage alloc] initWithData:mutData] retain];
[mutData release];
// HIDE THE ACTIVITY INDICATOR
// Make sure hidesWhenStopped is set to YES on the activity indicator.
[myUIActivityIndicatorView stopAnimating];
[myUIImageView setImage:image];
[image release];
}
Hope this helps!
Related
I am trying to use SVProgressHUD with cocoapods. I have install it in my xcode workspace following numerous tutorials online. It seems simple enough to use, but I cannot get it to work. Here my code to load data from rottentomatoes app. I want to show "loading" while the network request is doing its thing. I am wondering if this is ui thread issue ? I added a sleep inside the network request because the results were coming back too fast !
- (void) reload{
[SVProgressHUD showWithStatus:#"Updating" maskType:SVProgressHUDMaskTypeBlack];
NSString *url = #"http://api.rottentomatoes.com/api/public/v1.0/lists/dvds/top_rentals.json?";
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
[NSThread sleepForTimeInterval:3];
NSDictionary *object = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSMutableArray *array = [[NSMutableArray alloc]initWithArray:[object objectForKey:#"movies"]];
self.movies = [[NSMutableArray alloc]init];
for(NSDictionary *item in array){
SFMovie *movie = [[SFMovie alloc]initWithDictionary:item];
[self.movies addObject:movie];
}
[SVProgressHUD dismiss];
[self.tableView reloadData];
}];
}
EDIT for comment:
The reload is called on both init methods (since I am using storyboard for this project)
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
[self reload];
}
return self;
}
- (id) initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if(self){
[self reload];
}
return self;
}
Second EDIT:
I added a pull down to refresh and the "updating ..." shows up when I pull the tableview down. But it does not show up on init.
- (void)viewDidLoad
{
[super viewDidLoad];
UIRefreshControl *refreshControl = [UIRefreshControl new];
[refreshControl addTarget:self action:#selector(refresh:) forControlEvents:UIControlEventValueChanged];
refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:#"Pull to refresh..."];
self.refreshControl = refreshControl;
}
- (void)refresh:(UIRefreshControl *)sender{
[self reload];
[sender endRefreshing];
}
So I guess I cannot run UI stuff on init methods as the view is not ready. I am now calling the reload method on viewdidload and it works file. Thank you John !
In any init methods, messing with the view hierarchy is tricky business -- keep that logic in viewDidLoad to avoid any of those modifications from being wiped (especially from storyboard initialization).
My app is a messaging app, it can also send image files. I am just uploading as image on web server and on other side just sending its url, with NSURLConnection i am trying to download an image with UIProgressView as a download indicator, here is my code:
This method is called when download button in uitableview is clicked, it removes the download button, add uiprogressview and start downloading
-(void)downloadImage:(UIButton *)link
{
UITableViewCell *cell = (UITableViewCell*)[link superview];
NSIndexPath *pathToCell = [tView indexPathForCell:cell];
NSMutableDictionary *checkItHasFile = [messages objectAtIndex:pathToCell.row];
NSString *str = [checkItHasFile objectForKey:#"hasFile"];
if([str isEqualToString:#"1"])
{
progress = [[UIProgressView alloc]initWithProgressViewStyle:UIProgressViewStyleBar];
progress.frame = CGRectMake(10, 50, 160, 30);
progress.progress = 0.0;
//progress.center = CGPointMake(23,21);
[cell addSubview:progress];
}
UIButton *view = [[UIButton alloc]init];
NSArray *subviews = [cell subviews];
for (view in subviews)
{
if([view isKindOfClass:[UIButton class]])
{
[view removeFromSuperview];
}
}
//
NSString *linkToPass = [NSString stringWithFormat:#"THE URL"];
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:linkToPass]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection)
{
nsmd = [[NSMutableData alloc]init];
}
else
{
NSLog(#"Connection to server failed!");
}
...
This method is a NSURLConnection delegate to indicate about response, in this I am calculating response size
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.resourceData setLength:0];
self.filesize = [NSNumber numberWithLongLong:[response expectedContentLength]];
}
This method is a NSURLConnection delegate to indicate about received data, I am updating progress bar by doing some calculation
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
resourceData = [[NSMutableData alloc]init];
[self.resourceData appendData:data];
NSNumber *resourceLength = [NSNumber numberWithUnsignedInteger:[self.resourceData length]];
[self.progress setProgress:[resourceLength floatValue] / [self.filesize floatValue] animated:YES];
}
I want to know when downloding is finished so i can also remove progress view for that the delegate method is connectionDidFinishLoading:connection of NSURLConnection.
Problem is it immediately triggers, so when progress view is animating the download progress this method also executes, if I am removing the progress view here, the progress view will disappear immediately with out indicating a complete download progress.
How to solve this issue?
Define a method that removes the progressbar, then in your - (void)connectionDidFinishLoading:(NSURLConnection *)connection implementation, if the resource length is less than a certain amount you decide, call the remove method with a delay:
[self performSelector:#selector(removeProgressBar) withObject:nil afterDelay:2];
otherwise call the same method without any delay.
I'm trying to show a custom MBProgressHUD while downloading a list of URLs with SDWebImagePrefetcher using NSURLConnection methods.
SDWebImagePrefetcher has a method that, when called, shows in console the progress of the images download.
Now, i would like to show that NSLog progress in the custom MBProgressHUD and I would like the HUD to stay on screen until the process is done, but I don't know how to do it and plus, when my NSURLConnection methods are called, it shows the initial HUD (Connnecting), then quickly jumps to "Complete", even if the images still needs to be downloaded.
Here's my code:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
HUD.mode = MBProgressHUDModeDeterminate;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
HUD.labelText = #"Loading";
HUD.detailsLabelText = #"Downloading contents..."; //here, i would like to show the progress of the download, but it seems to jump this part
HUD.dimBackground = YES;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//arr = array which holds a plist
NSMutableArray *array = [[[NSMutableArray alloc]init]autorelease];
for (NSDictionary *dict in arr) {
for (NSDictionary *val in [dict valueForKey:STR_ROWS]) {
[array addObject:[val objectForKey:#"image"]];
}
}
[prefetcher prefetchURLs:array];
HUD.customView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"checkmark.png"]] autorelease];
HUD.mode = MBProgressHUDModeCustomView;
HUD.labelText = NSLocalizedString(#"Completed",#"Completed!");
HUD.detailsLabelText = nil;
HUD.dimBackground = YES;
[HUD hide:YES afterDelay:2];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[HUD hide:YES];
UIAlertView *alertView = [[[UIAlertView alloc] initWithTitle:#"Connection Failed message:[NSString stringWithFormat:#"Connection to the remote server failed with error:\n %#\n Try again in a while"),[error localizedDescription]] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil] autorelease];
[alertView show];
}
I tried to look into the examples, but didn't find out how to do what i want to do.
EDIT
HUD Setup:
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 0){
[alertView dismissWithClickedButtonIndex:0 animated:YES];
}
else{
NetworkStatus internetStatus = [internetReachable currentReachabilityStatus];
switch (internetStatus) {
case NotReachable:
{
//not reachable
break;
}
case (ReachableViaWWAN):
{
//reachable but not with the needed mode
break;
}
case (ReachableViaWiFi):{
HUD = [[MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES]retain];
HUD.delegate = self;
HUD.dimBackground = YES;
HUD.labelText = #"Connecting...";
NSURL *URL = [NSURL URLWithString:#"http://mywebsite.com/myPlist.plist"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
[connection release];
break;
}
default:
break;
}
}
}
Any ideas?
In connection:didReceiveResponse: you must record how large the download is,
for example in self.responseSize. Then, in connection:didReceiveData: you
must append the data you just got to the data you previously got, and update the progress:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
HUD.mode = MBProgressHUDModeDeterminate;
HUD.labelText = #"Loading";
HUD.detailsLabelText = #"Downloading contents...";
HUD.dimBackground = YES;
// Define responseSize somewhere...
responseSize = [response expectedContentLength];
myData = [NSMutableData data];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[myData appendData:data];
HUD.progress = (float)myData.length / responseSize;
}
I am using Lazy loading in my application.I want to Download images from url which starts from https i am not getting images using this so is there any steps we have to perform or something else please guide me what should i do?
#import "Hello_SOAPViewController.h"
#interface NSURLRequest (withHttpsCertificates)
+ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString*)host;
+ (void)setAllowsAnyHTTPSCertificate:(BOOL)allow forHost:(NSString*)host;
#end
#implementation Hello_SOAPViewController
NSMutableData *webData;
- (void)viewDidLoad {
//////////////////////////////////////////////////
//Web Service Call
////////////////////////////////////
NSURL *url = [NSURL URLWithString:#"https://192.168.1.105/HelloService/Service.svc/username(user)/password(xxx)"];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0];
[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[url host]];
[theRequest addValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[theRequest setHTTPMethod:#"GET"];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if(theConnection) {
webData = [[NSMutableData data] retain];
}
else {
NSLog(#"theConnection is NULL");
}
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[webData setLength: 0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[webData appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"ERROR with theConnection:%#",[error description]);
if ([error code] == -1001 ){//isEqualToString:#"timed out"]) {
UIAlertView *alertView = [[[UIAlertView alloc] initWithTitle:#"Connection Error" message:#"Server Unresponsive" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil] autorelease];
[alertView show];
}else{
UIAlertView *alertView = [[[UIAlertView alloc] initWithTitle:#"Connection Error" message:#"Check your internet connection " delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil] autorelease];
[alertView show];
}
[connection release];
[webData release];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"DONE. Received Bytes: %d", [webData length]);
///////////////////////
//Process Your Data here:
///////////////////////
[connection release];
[webData release];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
This piece of code:
- (IBAction) getXML {
goButton.enabled = NO;
[self performSelectorInBackground:#selector(parseInBackground) withObject:nil];
}
- (void)parseInBackground {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
xmlParser = [[XMLParser alloc] init];
NSURL *xmlurl = [[NSURL alloc] initWithString:#"http://www.mysite.com/myfile.xml"];
[xmlParser fetchXMLFromURL:xmlurl];
[self performSelectorOnMainThread:#selector(didFinishXMLParsing) withObject:nil waitUntilDone:YES];
[xmlurl release];
[pool drain];
}
- (void)didFinishXMLParsing {
goButton.enabled = YES;
}
triggers this code:
- (void)fetchXMLFromURL:(NSURL *)xmlurl {
XMLData = [[NSMutableData alloc] init];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:xmlurl];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection release];
[request release];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
}
However, when I step through it in debug, as soon as it gets to '}' in fetchXMLFromURL it returns to the line:
[self performSelectorOnMainThread:#selector(didFinishXMLParsing) withObject:nil waitUntilDone:YES];
and the connection to the URL which fetches the XML never actually gets triggered. Anyone any idea why?
This revised version seems to be working correctly, can anyone spot any potential issues?
- (void)fetchXMLFromURL:(NSURL *)xmlurl {
XMLData = [[NSMutableData alloc] init];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
XMLData = [NSData dataWithContentsOfURL:xmlurl];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
[self startParsingXML];
}
- (void) startParsingXML
{
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:XMLData];
xmlParser.delegate = self;
[xmlParser parse];
[xmlParser release];
}
Revised again, hopefully correct now
- (void)fetchXMLFromURL:(NSURL *)xmlurl {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
XMLData = [[NSData alloc] initWithContentsOfURL:xmlurl];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
[self startParsingXML];
}
- (void) startParsingXML
{
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:XMLData];
xmlParser.delegate = self;
[xmlParser parse];
[xmlParser release];
}
Yep. The NSURLConnection method you're using is asynchronous itself—it calls back to a couple of different delegate methods (as specified by the NSURLConnectionDelegate protocol) as it gets data. You might, in this case, be best off using the +sendSynchronousRequest:returningResponse:error: method, or NSData's +dataWithContentsOfURL:options:error:, as both of those will block execution on your background thread (which is fine) until they've finished getting your data. Also, make sure you set the network activity indicator's visibility before starting a synchronous request.
You're releasing the NSURLConnection as soon as you create it, which means it is immediately deallocated. You shouldn't release the NSURLConnection until you receive a delegate message that tells you the operation is complete, or has failed for some reason.