Any examples available that update UIImageView images dynamically at a high rate? These images are received on a IP connection in a NSThread. But they don't update when I do imageView.image = newImageRecd;
EDIT: All data is recd on main thread
Appreciate any help/pointers.
For iOS 4 and later, you can use Grand Central Dispatch (GCD) to load images on a background thread. Here's a link that explains this process fully:
http://blog.slaunchaman.com/2011/02/28/cocoa-touch-tutorial-using-grand-central-dispatch-for-asynchronous-table-view-cells/
The tutorial above provides this code:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
dispatch_sync(dispatch_get_main_queue(), ^{
[[cell imageView] setImage:image];
[cell setNeedsLayout];
});
});
I've been using this myself to load images from a remote server, and it's working great.
UI updates need to be performed on the main thread. If you are running the connection on a different thread, use:
[self performSelectorOnMainThread:#selector(updateImageView:) withObject:newImageRecd waitUntilDone:FALSE];
where updateImageView: is the method used to set the image view's image property:
-(void)updateImageView:(UIImage *)image
{
[self.imageView setImage:image];
}
Related
I got a custom UITableViewcell which should load an Image when use that code:
- (void)setImageWithURL:(NSURL *)URL
{
dispatch_queue_t taskQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(taskQ, ^{
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:URL]];
imageView.image = image;
NSLog(#"test");
});
}
It loads the Image very fast, so I get the response "test" very fast, but it needs to load IN the tableView. Also if I select the row it loads the Image.
How can I fix this issue?
Thanks
As long as setImageWithURL: is called as an effect of cellForRowAtIndexPath, then it should should show up as you expect... however, you need to call imageView.image = image; on the main thread. This goes for ALL UI related tasks. So you'll want:
- (void)setImageWithURL:(NSURL *)URL
{
dispatch_queue_t taskQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(taskQ, ^{
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:URL]];
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = image;
NSLog(#"test");
});
});
}
EDIT:
Using NSOperationQueue.
Create an operation queue as a member of your view controller.
m_operationQueue = [NSOperationQueue new];
m_operationQueue.maxConcurrentOperationCount = 1 // if you want to load images in order;
Make sure you cancel operations when the view unloads and when the table will refresh using
[m_operationQueue cancelAllOperations];
Do your thing in setImageWithURL
- (void)setImageWithURL:(NSURL *)URL
{
[m_operationQueue addOperationWithBlock:^{
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:URL]];
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = image;
NSLog(#"test");
});
}];
}
EDIT again:
Sorry, forgot that setImage was in your custom table cell. You can either:
1) make it a static object that you initialize in the +(void)initialize; method of your cell
2) make it a static object available via class method on your view controller
2) make a singleton instance that is available to your table cell
The design preference is up to you and depends what other practices you've been using if you want to be consistent.
You should call [tableView reloadData] when you finish downloading the image.
There is a similar question with an accepted answer, but also it has a suggestion to use SDWebImage. Take a look: How to get UITableViewCell images to update to downloaded images without having to scroll UITableView
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.
NSURL * imageURL = [NSURL URLWithString:imageurldata];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
image1 = [UIImage imageWithData:imageData];
[image1 retain];
I wrote above code for uploading the image in iPhone, and i m showing a new view in which i am showing this image but image takes time to appear till then the screen is blank. We are taking the image from url and storing it in an object. Is there any to show the image and view at the same time?
Try this async approach:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSLog(#"Screen %# - pauseBannerFileImage download starts", self.name);
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:newUrlForImage]]];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"!-Screen %#-!pauseBannerFileImage downloaded", self.name);
self.imageView.image = image;
});
});
how to handle tiling of images on the fly.
You will only need the TileImageView classes and use it(not UIImageView as it handles data downloading asynchronously) as follows....
TileImageView *tileImageView = [[TileImageView alloc]initWithFrame:<myFrameAsPerMyNeeds>];
[tileImageView setTag:<this is the identifier I use for recognizing the image>];
[myImageScrollView addSubView:tileImageView];
[tileImageView startImageDownloading:imageurldata];
[tileImageView release];
Thanks,
As far as my knowledge, it will take some time to download the data from server.there is one way for covering the time delay is show the UIActivityIndicatorView while downloading the image data
You may want to initialize the view before it is actually needed so the load may have already occurred by the time a user needs a view.
I'm implementing a simple twitter client for the iPhone using a UITableView. I fetch the picture of each twitter user in my feed when their cell appears in tableView: cellForRowAtIndexPath:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *profileImage = [tweet.user getProfileImageDataInContext:self.fetchedResultsController.managedObjectContext];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = profileImage;
});
});
Here is the code to fetch the image:
if (!self.profileImage)
{
sleep(2);
self.profileImage = [NSData dataWithContentsOfURL:[NSURL URLWithString:self.profileImageURL]];
//// if we recently scheduled an autosave, cancel it
[TwitterUser cancelPreviousPerformRequestsWithTarget:self selector:#selector(autosave:) object:context];
// request a new autosave in a few tenths of a second
[TwitterUser performSelector:#selector(autosave:) withObject:context afterDelay:0.2];
}
return [UIImage imageWithData:self.profileImage];
Here is the error I'm getting:
twitterClient[10743:15803] bool _WebTryThreadLock(bool), 0x59bac90: 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...
I think it's also worth mentioning that this happens when I scroll through the tableview very quickly when it hasn't been populated yet.
I would like the main UI to update upon completion of the download. The actual twitter app for iPhone does this quite well.
Any suggestions?
What’s the crash? A pretty standard pattern for things like this is
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do background stuff
dispatch_async(dispatch_get_main_queue(), ^{
// do main-queue stuff, like updating the UI
});
});
You're using GCD fine, your crash is happening because you're calling dataWithContentsOfURL in a background thread, which is not thread safe.
See: Does -dataWithContentsOfURL: of NSData work in a background thread?
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.