Objective-C UImage imageWithData Problem - iphone

I'm having a problem displaying an image in an image view, which is generated from data.
My program works as follows:
The user selects a row from a UITableView.
Upon selection, a new view (Event Description) is generated and has 3 NSStrings and an NSData object passed to it.
This data is displayed in the view, however a thread is also spawned to convert the NSData into an image and display it as without that there is a slight delay in displaying the description view.
This code works fine on the simulator, however when I run it on my iPhone and select a row, the first selection works fine, then all corresponding selections have a noticeable delay in displaying the image..
Here is my code:
- (void)viewDidLoad {
[super viewDidLoad];
//Display the selected events data
name.text = eventName;
date.text = eventDate;
description.text = eventDescription;
//Set the title of the navigation bar
self.navigationItem.title = eventName;
/* Operation Queue init (autorelease) */
NSOperationQueue *queue = [NSOperationQueue new];
/* Create our NSInvocationOperation to call loadDataWithOperation, passing in nil */
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(loadDataWithOperation)
object:nil];
/* Add the operation to the queue */
[queue addOperation:operation];
[operation release];
}
//Threaded operation
- (void) loadDataWithOperation {
//Set the event image
UIImage *img = [UIImage imageWithData: eventImageURLData];
image.image = img;
}
Does anyone have any idea whats causing this?
Thanks,
Jack

Simulator doesn't have memory constraints so it rarely flags the low memory problem. As such you're leaking NSOperationQueues. I see that you've marked it as autorelease but see no autorelease message. You should probably fix that. In addition to this, you should be sending the image update code to the main thread.

Related

objective - C : Loading image from URL?

Sorry for question title. I can not find a suitable title.
I have UITableView content images from url when i open the UITableView the View did not show until the images loaded and that takes along time.
I get the images from JSON by php.
I want to show the table and then images loading process.
This is code from my app:
NSDictionary *info = [json objectAtIndex:indexPath.row];
cell.lbl.text = [info objectForKey:#"title"];
NSString *imageUrl = [info objectForKey:#"image"];
cell.img.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]]];
[cell.img.layer setBorderColor: [[UIColor blackColor] CGColor]];
[cell.img.layer setBorderWidth: 1.0];
return cell;
Sorry my english is weak.
Perform the web request on a separate thread, to not block the UI. Here is an example using NSOperation. Remember to only update the UI on the main thread, as shown with performSelectorOnMainThread:.
- (void)loadImage:(NSURL *)imageURL
{
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self
selector:#selector(requestRemoteImage:)
object:imageURL];
[queue addOperation:operation];
}
- (void)requestRemoteImage:(NSURL *)imageURL
{
NSData *imageData = [[NSData alloc] initWithContentsOfURL:imageURL];
UIImage *image = [[UIImage alloc] initWithData:imageData];
[self performSelectorOnMainThread:#selector(placeImageInUI:) withObject:image waitUntilDone:YES];
}
- (void)placeImageInUI:(UIImage *)image
{
[_image setImage:image];
}
You have to use NSURLConnection and NSURLRequest. First create and show your empty table view (maybe with placeholder images, that are stored locally in the app). Then you start sending requests. These requests will run in the background and you (the delegate) will be notified when a request is completed. After that you can show the image to the user. Try not to load all the images at once if you have a lot of them. And don't load the ones that are invisible to the user, only load those if he scrolls down.
There is a UITableView lazy image loading example that Apple provided: https://developer.apple.com/library/ios/#samplecode/LazyTableImages/Introduction/Intro.html
Hopefully it's what you were looking for
This is among very common thing we do in our application.
You simply can have store the URLs in a persistent store e.g array or db & can get the images using Operation queue to download faster. You can set the priorities, cancel operations at anytime etc. Also, the application respond time will be quicker.

UIImageView.image = mImage leak

I have thread2 loop where i do assembly (create from raw bytes data) some UIImage
in every iteration of this loop
thread2loop()
{
//make UIIamge here
[self performSelectorOnMainThread:#selector(setUiImage) withObject:nil waitUntilDone:YES];
}
there and then i call setUIImage method on the main thread
- (void) setUiImage
{
self.imageView.image = nil;
self.imageView.image = mImage;
[mImage release];
}
it is working but the Instruments , leaks application shows to me that there are
UIImage leaks here and i do not know how to ##$! get rid of it! (im sad and little tired
and bored), help, what to do, tnx
Surround your threaded code with...
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//threaded code....
[pool release];
Classic producer/consumer problem. Your producer thread is probably outrunning the main thread (the consumer). I'd recommend keeping a queue of images (instead of the single mImage), guarded by a lock which you enqueue images onto (from your background queue), and dequeue images from your main queue. Or you could use GCD, which makes this even easier. Instead of using mImage to hold onto the created image, you could just use a block which would retain the image and then set it on your image view in the main queue. Something like:
thread2loop() {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
while (...) {
__block id self_block = self; // (don't want to retain self in the block)
UIImage *img = [[UIImage alloc] initWithCGImage:quartzImage scale:1.0 orientation:UIImageOrientationUp];
dispatch_async(dispatch_get_main_queue(), ^{
block_self.imageView.image = img;
[img release];
});
}
[pool drain]; // release is outdated for autorelease pools
}
Warning: Doing this too much will quickly run the device out of memory and cause your app to be killed. You probably want to make sure that your use of this technique is limited to creating a small number of images.

How to know when UIimageView finished loading?

In my view controller, how can I know when a certain UIImageView has finished loading (large jpeg from documents directory)? I need to know so that I can then swap a placeholder low-res imageview with this hi-res imageview. Do I need to create a custom callback to know this? Any way is fine.
By the way, here is a snippet of code where I load the image:
NSString *fileName = [NSString stringWithFormat:#"hires_%i.jpg", currentPage];
NSString *filePath = [NSString stringWithFormat:#"%#/BookImage/%#", [self documentsDirectory], fileName];
hiResImageView.image = [[[UIImage alloc] initWithContentsOfFile:filePath] autorelease];
UIImageView isn't doing any loading at all. All the loading is being done by [[UIImage alloc] initWithContentsOfFile:filePath], and your thread is blocked while the file is loaded (so the load is already complete by the time that call finally returns).
What you want to do is something like this:
- (void)loadImage:(NSString *)filePath {
[self performSelectorInBackground:#selector(loadImageInBackground:) withObject:filePath];
}
- (void)loadImageInBackground:(NSString *)filePath {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];
[self performSelectorOnMainThread:#selector(didLoadImageInBackground:) withObject:image waitUntilDone:YES];
[image release];
[pool release];
}
- (void)didLoadImageInBackground:(UIImage *)image {
self.imageView.image = image;
}
You would set up self.imageView to display the low-res image and then call loadImage: to load the high-res version.
Note that if you call this repeatedly before didLoadImageInBackground: gets called from earlier calls, you may cause the device to run out of memory. Or you might have the image from the first call take so much longer to load than image from the second call that didLoadImageInBackground: gets called for the second image before it gets called for the first. Fixing those issues is left as an exercise for the reader (or for another question).

mapview is not showing before it load all data

i have one application it has mapview .
problem is that i want to show map. means right now when all data loaded then it show mapview. i want to show mapview before it loaded . how can i do this ?
Edit:
for show map i use this [self performSelector:#selector(methoname) withObject:nil afterDelay:0.02];
but by this other problem generate .problem is ,it show me two times activity indicator. any other thing ,like performselector but without activity indicator.
i dont want to show this thing.
You want to look at multithreading.
Basically, you should be retrieving your data in a background thread. If you do what you're doing right now (loading everything in the main thread) the UI cannot be updated until everything is loaded.
So, do something like this:
-(void)viewDidLoad{
mapView.hidden = NO;
[self performSelectorInBackground:#selector(getExtraMapData)];
}
-(void)getExtraMapData{
NSAutoReleasePool *pool = [[NSAutoreleasePool alloc] init];
[self downloadMapData];
[map addTheDataIDownloaded];//I've never used the mapView,
// so I don't know it's methods.
[pool release];
}
-(void)downloadMapData{
NSURL *mapDataURL = [NSURL URLWithString:#"http://aURLWithInfoForMyMap.com"];
NSData *mapData = [NSData dataWithContentsOfURL:mapDataURL];
}

NSThread problem in iOS

I have an app that loads multiple thumbnail images into a UIScrollVIew. This is a lengthy operation, and so as not to block up the display of the rest of the UI, I am running it in a separate thread. This works fine the first time at application launch, but later a new set of images needs to be loaded into the UIScrollView. When I detach a thread a second time the app crashes (sometimes). Code follows:
// this call is in a separate method
[NSThread detachNewThreadSelector:#selector(addThumbnailsToScrollView) toTarget:self withObject:nil];
// this is the main entry point for the thread
- (void) addThumbnailsToScrollView {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Top-level pool
// now place all the thumb views as subviews of the scroll view
float xPosition = THUMB_H_PADDING;
int pageIndex = 0;
for (Page *page in self.pages) {
// get the page's bitmap image and scale it to thumbnail size
NSString *name = [page valueForKey:#"pageBackground"];
NSString *basePath = [[NSBundle mainBundle] pathForResource:page.pageBackground ofType:#"jpg" inDirectory:nil];
UIImage *thumbImage = [UIImage imageWithContentsOfFile:basePath];
thumbImage = [thumbImage imageScaledToSize:CGSizeMake(80, 100)];
// create a ThumbImageView for each page and add it to the thumbnailScrollView
if (thumbImage) {
ThumbImageView *thumbView = [[ThumbImageView alloc] initWithImage:thumbImage];
[thumbView setDelegate:self];
[thumbView setImageName:name];
[thumbView setImageSize:CGSizeMake(80, 100)];
[thumbView setPageIndex:pageIndex];
pageIndex ++;
CGRect frame = [thumbView frame];
frame.origin.y = 0;
frame.origin.x = xPosition;
[thumbView setFrame:frame];
[thumbnailPagesScrollView addSubview:thumbView];
[thumbView release];
xPosition += (frame.size.width + THUMB_H_PADDING);
}
}
[self hightlightThumbnailPageAtIndex:0];
[(UIActivityIndicatorView *)[thumbnailPagesScrollView.superview viewWithTag:100] stopAnimating];
[pool release]; // Release the objects in the pool.
}
I thought that a detached thread exits as soon as the main entry routine was completed. Wouldn't the second call to detach a thread be a new thread? Why is the app crashing, but sometimes not?
Thanks
Jk
You cannot touch UIKit (meaning UIScrollVIew) in a secondary thread - you need to reorganize so that the fetch takes place in a secondary thread but you make a NSData object (containing the image binary) available to your primary thread for each thumbnail so that it can do everything related to actually displaying them.
Apple repeatedly warn in documentation that UIKit is not thread-safe.
I would suggest adding the thumbView to the thumbnailPagesScrollView on the main thread rather than a separate thread. There might be issues on the retain count of the object across threads. There is a convenience method performSelectorOnMainThread I think it is to do that. You could pass thumbView to that and then add it to the subview.
Alternatively you could do the whole if statement on the main thread as thats not the thing that will interrupt the user.
Also with your activity indicator this should be stopped on the main thread. Everything UI related should be done on the main thread.