Help loading large Images in UIWebview? - iphone

I am loading images in UIWebview using the following code. For each image (while scrolling my scrollview) a function is called to load the image. The problem is that when I am continuously scrolling up to 30 images the application crashes. What may be the reason? My images are 1300 by 1200 pixels. Please help me find a solution.
- (void) loadCatalogImage
{
#try{
// if(imgView.image != nil)
// return;
// [global_imgProgress startAnimating];
//NSLog(#"image loading at = %#, %d", baseURL, 2 + pageNo);
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
//NSArray *array = [global_ContentString componentsSeparatedByString:#"###"];
NSArray *array1 = [catalogURL componentsSeparatedByString:#"&"];
//NSLog(#"baseURL = %#",baseURL);
NSLog(#"loading catalog image(method: loadCatalogImage).......%#%#",baseURL, [[[array1 objectAtIndex:0] componentsSeparatedByString:#"##"] objectAtIndex:0]);
zoomedImageURL = [NSString stringWithFormat:#"%#%#", baseURL, [[[array1 objectAtIndex:0] componentsSeparatedByString:#"##"] objectAtIndex:1]];
// NSLog(#"Catalog ZOOM URL = %#", zoomedImageURL);//[[array1 objectAtIndex:0] componentsSeparatedByString:#"##"]);//[[[array objectAtIndex:[[global_CatalogRef objectAtIndex:pageNo] intValue]] componentsSeparatedByString:#"##"] objectAtIndex:3]);
[zoomedImageURL retain];
NSLog(#"aaaaaaa = %#",zoomedImageURL);
[webView loadRequest:
[NSURLRequest requestWithURL:
[NSURL URLWithString:
[NSString stringWithFormat:#"%#%#",baseURL, [[[array1 objectAtIndex:0] componentsSeparatedByString:#"##"] objectAtIndex:1]
]
]]
];
}
}

Any app that loads up enough resources to occupy so much memory will crash. Apple has several demo projects that deal specifically with loading a large image by breaking it down into smaller bits ci suggest you take a look at them.

Your images are 1300 x 1200 and you're loading 30 of them? You may WILL be running out of memory. If your app is ignoring memory warnings, and hogging up memory, the iOS system may be forcing your app to quit. As I mention in my comment below, that many images, that size, would be about 180megs. Far too big.
Do you really need to deliver such large images to an iphone app, and so many of them?

Can you put a crash log please? If possible Enable the Environment variable 'NSZombieEnabled' and then run the application and have a look at the crash log. This will help us to get the exact problem. By the way why are you creating and retaining "zoomedImageURL"?

Related

Taking time to load the image from URL to UIImageview

I am using this code for displaying the image from URL to UIImageview
UIImageView *myview=[[UIImageView alloc]init];
myview.frame = CGRectMake(50, 50, 320, 480);
NSURL *imgURL=[[NSURL alloc]initWithString:#"http://soccerlens.com/files/2011/03/chelsea-1112-home.png"];
NSData *imgdata=[[NSData alloc]initWithContentsOfURL:imgURL];
UIImage *image=[[UIImage alloc]initWithData:imgdata];
myview.image=image;
[self.view addSubview:myview];
But the problem is that its taking too long time to display the image in imageview.
Please help me...
Is there any method to fast the process...
Instead of dispatch_async, Use SDWebImage for caching the images.
This is best I have seen...
The problem of dispatch_async is that if you lost focus from image, it will load again. However SDWebImage, Caches the image and it wont reload again.
The answers given to me on my own question Understanding the behaviour of [NSData dataWithContentsOfURL:URL] inside the GCD block does makes sense.So be sure that if you use [NSData dataWithContentsOfURL:URL] inside the GCD(as many developers do these days) is not a great idea to download the files/images.So i am leaning towards the below approach(you can either use NSOperationQueue).
Load your images using [NSURLConnection sendAsynchronousRequest:queue:completionHandler: then use NSCache to prevent downloading the same image again and again.
As suggested by many developers go for SDWebimage and it does include the above strategy to download the images files .You can load as many images you want and the same URL won't be downloaded several times as per the author of the code
EDIT:
Example on [NSURLConnection sendAsynchronousRequest:queue:completionHandler:
NSURL *url = [NSURL URLWithString:#"your_URL"];
NSURLRequest *myUrlRequest = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:myUrlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if ([data length] > 0 && error == nil)
//doSomething With The data
else if (error != nil && error.code == ERROR_CODE_TIMEOUT)
//time out error
else if (error != nil)
//download error
}];
Use Dispatch queue to load image from URL.
dispatch_async(dispatch_get_main_queue(), ^{
});
Or add a placeholder image till your image gets load from URL.

How to improve images loading issues. is there any cache like solution

I have built an iOS app using Parse.com.
In my app i have displayed a lot of data from network.
To load the data speedily i used cache kPFCachePolicyCacheThenNetwork. It loads data fine but while scrolling images are takes some amount of time to load.
First white image and then image loads.
Is there any solution like cache to display images in imageviews with out interruption.
Thanks.
You can use SDWebImage for doing this.
Also you can use NSCache for caching purpose.
Yes you can use AFNetworking UIImageview category and you are sorted
you can also disable caching of a particular image usually needed if you edit an existing image and then dwnload it from same URL, by manipulagting its
-(void)setImageWithURL:(NSURL *)url
placeholderImage:(UIImage *)placeholderImage
as mentioned in this blog
An other option could be ASImageView with facility to refresh cache.
I write the code with the help of gcd , you just have to pass the object of imageView and pass the url , it manages all the things, like cache.
-(void)downloadingServerImageFromUrl:(UIImageView*)imgView AndUrl:(NSString*)strUrl{
strUrl = [strUrl encodeUrl];
NSString* theFileName = [NSString stringWithFormat:#"%#.png",[[strUrl lastPathComponent] stringByDeletingPathExtension]];
NSFileManager *fileManager =[NSFileManager defaultManager];
NSString *fileName = [NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:#"tmp/%#",theFileName]];
imgView.backgroundColor = [UIColor darkGrayColor];
UIActivityIndicatorView *actView = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
[imgView addSubview:actView];
[actView startAnimating];
CGSize boundsSize = imgView.bounds.size;
CGRect frameToCenter = actView.frame;
// center horizontally
if (frameToCenter.size.width < boundsSize.width)
frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2;
else
frameToCenter.origin.x = 0;
// center vertically
if (frameToCenter.size.height < boundsSize.height)
frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2;
else
frameToCenter.origin.y = 0;
actView.frame = frameToCenter;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSData *dataFromFile = nil;
NSData *dataFromUrl = nil;
dataFromFile = [fileManager contentsAtPath:fileName];
if(dataFromFile==nil){
dataFromUrl=[[[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:strUrl]] autorelease];
}
dispatch_sync(dispatch_get_main_queue(), ^{
if(dataFromFile!=nil){
imgView.image = [UIImage imageWithData:dataFromFile];
}else if(dataFromUrl!=nil){
imgView.image = [UIImage imageWithData:dataFromUrl];
NSString *fileName = [NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:#"tmp/%#",theFileName]];
BOOL filecreationSuccess = [fileManager createFileAtPath:fileName contents:dataFromUrl attributes:nil];
if(filecreationSuccess == NO){
NSLog(#"Failed to create the html file");
}
}else{
imgView.image = [UIImage imageNamed:#"NO_Image.png"];
}
[actView removeFromSuperview];
[actView release];
[imgView setBackgroundColor:[UIColor clearColor]];
});
});
}
Here is the code for loading image in background . check this Code
you should use EGOImageLoading(EGOImageLoading) for lazy loading of images and can save images using NSUserDefaults.
It is best way i advice as i have used it in many projects.
Sorry to say it, but Parse is already doing all these things. Parse uses AFNetworking to get the images, puts them in an NSCache, and uses disk caching very aggressively so that redundant network trips are completely avoided (the version is part of the URL, so Parse doesn't even need to use an if-modified-since GET).
The biggest issue you might be encountering is that PFFile throttles downloads to only download 3 images at a time. After experimentation this was the best number because it ensured incremental feedback to the user and avoided a nasty issue where some iOS devices would get very poor performance after too many threads. The best advice I could offer you would be to prefetch some images if possible. As mentioned earlier, PFFile images are very aggressively cached. The kPFCachePolicy... stuff only affects queries which might grow stale; the images those queries point to are always cached (though only initially downloaded on demand).

Loading text for UIButton and UITextView in background (Simple CMS)

I kinda want a CMS feature in my iPhone app. I want to load text and an image from the internet and apply them to a UIImageView, UIButton, and UITextView. However I am getting an error. Currently the code works fine with the loading of the image. I had the text code in the viewDidAppear code however the user wasn't able to interact with the screen until the text loaded so I moved it to the loadInfo method with the Image, however it didn't like this and gave me a Bad Access error. And printed the following in the console:
2011-05-08 21:26:13.770 Fraction Calculator Lite[2184:6b03] bool _WebTryThreadLock(bool), 0x62338d0: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
Anyone know what might be my issue?
Thanks!
This is my code:
- (void)viewDidAppear:(BOOL)animated {
[self performSelectorInBackground:#selector(loadInfo) withObject:nil];
}
-(void) loadInfo {
NSAutoreleasePool *arPool = [[NSAutoreleasePool alloc] init];
NSError *error;
NSString *internetTest = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://lindahlstudios.com/cms/internettest.txt"] encoding:NSUTF8StringEncoding error:&error];
if ([internetTest isEqualToString: #"Internet Connection Complete"]) {
UIImage *img1 = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://lindahlstudios.com/cms/adimage.png"]]];
[image1 setImage:img1];
[img1 release];
NSString *string = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://lindahlstudios.com/cms/adtext.txt"] encoding:NSUTF8StringEncoding error:&error];
NSString *string2 = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://lindahlstudios.com/cms/price.txt"] encoding:NSUTF8StringEncoding error:&error];
[adText setText:string]; //Thread 8: Program received signal: "EXC_BAD_ACCESS"
[price setTitle:string2 forState:UIControlStateNormal];
}
[arPool release];
}
You cannot invoke UIKit operations on background thread. I would suggest you get the data on background thread and then update it in the main thread.
Use blocks for ease in coding,
Once you retrieve NSData in background thread, simple assign it to the UIImage etc in the main thread via blocks,
//get NSData from URL
dispatch_async(dispatch_get_main_queue(), ^{
//set assignments for UIImage here.
});

Help with memory problem on iOS/iPad

In my app, I have a bunch of different image packs to download. The images are downloaded from my website one by one. The packs contain anywhere from 100-1500 images and each image is about 100KB-200KB.
When downloading the images, I have a method that selects the images to download, then I have a second method that does the actual downloading based on parameters sent from the main method. Here is the code for the download method:
-(BOOL) downloadImageName:(NSString *)imageName ImageGroup:(NSString *)imageGroup AndRow:(int)row {
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSURL *downloadURL = [NSURL URLWithString:[NSString stringWithFormat:#"http://www.website.com/%#_0001.jpg",imageName]];
NSData *data = [NSData dataWithContentsOfURL:downloadURL];
NSString *savePath = [NSString stringWithFormat:#"%#/%#^%#_%#.jpg",docsPath,currentServerCycle,imageGroup,imageName];
BOOL downloaded = [data writeToFile:savePath atomically:YES];
if (downloaded)
return YES;
}
else {
return NO;
}
}
The problem I am having is this:
When I run this with performance tools looking at allocations I'm seeing that the app is keeping mallocs (NSConcreteData) each time an image is downloaded and only releasing them when the main method (the one calling this download method) completes. Thats fine for the smaller image packs, but the larger ones are obviously crashing after my total allocations hit something like 300+MB (the normal amount of allocations in my app is about 3mb).
They arent leaking, because once the image pack is downloaded and the method ends, all the mallocs are gone.
I have tried manually allocing and releasing the NSData *data but it has no effect.
Perhaps the autorelease pool the objects are in is not being drained until the main method returns. You might try adding NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; at the beginning of your download method, then [pool drain]; right before you return from the download method. You need to make sure you release the pool before you return no matter where you return otherwise you'll leak the pool.
Also, just as a point of standard Objective-C style, your method should be named:
(BOOL)downloadImageName:(NSString *)imageName imageGroup:(NSString *)imageGroup andRow:(int)row
with lowercase letters to start the "imageGroup:" and "andRow:" argument names.

How can I download images without holding up everything else?

I'm making an app that allows you to browse through pictures from a website. I'm currently downloading the images using:
UIImage *myImage = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:url]]];
which works great, but can be time consuming. I start off by downloading 20 images, but I can't do anything until after the 30 or so seconds it takes to download them all.
This one time wait isn't all that bad, but if I want to download the 21st-40th images, I would have to wait another 30 seconds.
Basically, is there a way I can download these images one at a time without holding up any of my animations?
Thanks.
Sure, put the download task in a thread, and use a callback to let your program know when each image is finished. Then you can draw your images as they finish loading, and not hold up the rest of the app. This link has a template that you can use as an example.
Here's a quick and dirty example:
- (void)downloadWorker:(NSString *)urlString
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [[UIImage alloc] initWithData:data];
[self performSelectorOnMainThread:#selector(imageLoaded:)
withObject:image
waitUntilDone:YES];
[image release];
[pool drain];
}
- (void)downloadImageOnThread:(NSString *)url
{
[NSThread detachNewThreadSelector:#selector(downloadWorker:)
toTarget:self
withObject:url];
}
- (void)imageLoaded:(UIImage *)image
{
// get the image into the UI
}
Call downloadImageOnThread for every image you want to load, each will get its own thread, and you'll get calls to imageLoaded as each one completes.
While loading the image on a background thread is definately the solution, I'd use an NSOperation and an NSOperationQueue instead of dealing with the threads yourself (this is the way Apple recommend to deal with threading problems like this!)
The NSOperationQueue will deal with starting/stopping the threads nicely and you can choose how many to run at once etc. It's basically a the same as the other answers but you get a little more control.
There's a tutorial here that looks pretty good.
yeah you can use a secondary thread, and do a lot of work OR you could use things that apple gives us.
NSURLDownload, doesn't "lag" your main thread, You spawn it with a method and you set a endSelector, the endSelector will get called when the download is done.
Spawning a secondary thread for this is not really what you should do.
here you got some code from my app wich does it work perfectly without giving a beach ball of doom.
- (void)downloadAvatar:(NSString *)URL{
NSURL *url = [[NSURL alloc] initWithString:URL];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
[url release];
NSURLDownload *download = [[NSURLDownload alloc] initWithRequest:request delegate:self];
NSString *path = [[NSString alloc] initWithFormat:#"%#data/%#.jpg",[[BFAppSupport defaultSupport] bfFolderPath],[[xfSession loginIdentity] userName]];
[download setDestination:path allowOverwrite:YES];
[download release];
[path release];
[request release];
}
- (void)downloadDidFinish:(NSURLDownload *)download{
NSString *path = [[NSString alloc] initWithFormat:#"%#data/%#.jpg",[[BFAppSupport defaultSupport] bfFolderPath],[[xfSession loginIdentity] userName]];
NSData *imageData = [[NSData alloc] initWithContentsOfFile:path];
if( [imageData length] < 10 ){
[self performSelector:#selector(downloadAvatar:) withObject:#"http://media.xfire.com/xfire/xf/images/avatars/gallery/default/xfire160.jpg" afterDelay:0.0];
[imageData release];
[path release];
return;
}
NSImage *theImage = [[NSImage alloc] initWithData:imageData];
[imageData release];
[path release];
[yourImage setImage:theImage];
[theImage release];
}
- (void)download:(NSURLDownload *)aDownload didFailWithError:(NSError *)error{
NSLog(#"Avatar url download failed");
}
The code is a bit ugly, but its not hard to change it as you got the 3 things you need, the method that starts the download and 2 that handle or an error or the finish.
You can also use autoreleased objects some more, but in terms of performance I like using it without autoreleased objects when I can.