I'm creating a stack of images in my viewdidload method. The images are from PFFile's from parse so they contain the fileURL of my image data.
My problem is that these two lines of code are dramatically slowing down my app and killing my user experience:
//get scene object
PFObject *sceneObject = self.scenes[i];
//get the PFFile and filetype
PFFile *file = [sceneObject objectForKey:#"file"];
NSString *fileType = [sceneObject objectForKey:#"fileType"];
//check the filetype
if ([fileType isEqual: #"image"])
{
//get image
NSURL *imageFileUrl = [[NSURL alloc] initWithString:file.url];
NSData *imageData = [NSData dataWithContentsOfURL:imageFileUrl]; ********** these
imageView.image = [UIImage imageWithData:imageData]; ********************* lines
}
How do I get this image/these images (this is nested in a for loop) more quickly? I've already downloaded my PFObjects that contain the PFFiles and stored them locally.
I guess I really don't understand how file URL's operate.
Thank you.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSURL *imageFileUrl = [[NSURL alloc] initWithString:file.url];
NSData *imageData = [NSData dataWithContentsOfURL:imageFileUrl];
dispatch_get_main_queue(), ^{
imageView.image = [UIImage imageWithData:imageData];
});
});
Have not tested but this is the gist. Get the file loading off the main queue and make it asynchronous. The UI will not be bogged down because as soon as this queue is dispatched it's going to return and keep evaluating the rest of your application.
I am using somethig like this:
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[imageFrame addSubview:activityIndicator];
activityIndicator.center = CGPointMake(imageFrame.frame.size.width / 2, imageFrame.frame.size.height / 2);
[activityIndicator startAnimating];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData * imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:thumb]];
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [UIImage imageWithData:imageData];
img.image = image;
[imageOver addSubview:img];
[activityIndicator removeFromSuperview];
});
});
Related
I am trying to fetch images on a button click in Viewcontroller B .After the image is received as response,I use the url of images to populate the imageviews on viewcontroller.But,it is taking too long to get populated and also hangs while scrolling.Is there any way to scroll the view smoothly without freezing and get the response little early.
This line should not be in main queue
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[responseArr objectAtIndex:indexPath.row]valueForKey:#"CategoryImage"]]];
you may change to something like this
NSString *getImagePath = [documentsPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.jpeg",n1.Id]];
if([[NSFileManager defaultManager] fileExistsAtPath:getImagePath])
{
UIImage *img = [UIImage imageWithContentsOfFile:getImagePath];
cell.leftImageV.image =img;
[cell.activity stopAnimating];
cell.activity.hidden=YES;
}
else
{
[cell.activity startAnimating];
cell.activity.hidden=NO;
cell.leftImageV.image = nil;
NSLog(#"xdasdxsadxa %#",n1.mainImgStr);
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{
__block NSData * imageData = nil;
dispatch_sync(concurrentQueue, ^{
imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString:n1.mainImgStr]];
//Add the file name
[imageData writeToFile:getImagePath atomically:YES]; //Write the file
});
dispatch_sync(dispatch_get_main_queue(), ^{
if(imageData)
{
[self.collectionView reloadData];
}
});
});
}
Use dispatch async for image retrieve, namely, don't do it on main queue.
Apple Doc:
https://developer.apple.com/documentation/dispatch/1453057-dispatch_async
I have a block to download an convert an image to UIImage. this is my code
-(UIImage *)GetProfileImage
{
NSString *strimgBaseUrl=[[[NSBundle mainBundle] infoDictionary] objectForKey:#"BaseImageURL"];
NSString *strFilePath=[dm.dictUserProfile valueForKey:#"ImagePath"];
NSString *strImgURL=[NSString stringWithFormat:#"%#%#",strimgBaseUrl,strFilePath];
__block UIImage *imgProf=nil;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *imageURL = [NSURL URLWithString:strImgURL];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:imageData];
dispatch_async(dispatch_get_main_queue(), ^{
imgProf= image;
if(image!=nil){
imgProf=image;
}
});
});
return imgProf;
}
But this block is never execute.It goes to return __block UIImage *imgProf=nil; Why is that? Please help me.
Thanks
Code is working ok. This is the purpose of dispatch_async.
Form the documentation
#function dispatch_async
Calls to dispatch_async() always return immediately after the block has been submitted, and never wait for the block to be
invoked.
This is the opposite of dispatch_sync.
You could change the GetProfileImage like this
- (void)setProfileImageForImageView:(UIImageView *)imageView {
NSString *strimgBaseUrl=[[[NSBundle mainBundle] infoDictionary] objectForKey:#"BaseImageURL"];
NSString *strFilePath=[dm.dictUserProfile valueForKey:#"ImagePath"];
NSString *strImgURL=[NSString stringWithFormat:#"%#%#",strimgBaseUrl,strFilePath];
__block UIImage *imgProf=nil;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *imageURL = [NSURL URLWithString:strImgURL];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:imageData];
dispatch_async(dispatch_get_main_queue(), ^{
imgProf= image;
if(image!=nil){
// imgProf=image;
imageView.image = image;
}
});
});
}
Use a completion block instead, rather than returning from function:
-(void)GetProfileImageWithCompletion:(void (^)(UIImage *profileImage))completionHandler
{
NSString *strimgBaseUrl=[[[NSBundle mainBundle] infoDictionary] objectForKey:#"BaseImageURL"];
NSString *strFilePath=[dm.dictUserProfile valueForKey:#"ImagePath"];
NSString *strImgURL=[NSString stringWithFormat:#"%#%#",strimgBaseUrl,strFilePath];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *imageURL = [NSURL URLWithString:strImgURL];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:imageData];
dispatch_async(dispatch_get_main_queue(), ^{
if(image!=nil && completionHandler){
completionHandler(image);
}
});
});
}
And use like this:
[self GetProfileImageWithCompletion:^(UIImage *profileImage)
{
//if image exists assign to image view
}
];
I am fetching URL from web, and I want to show any message if image view can't get image from given URL.
Here is my Code.
NSString * image1 = [WebArray objectForKey:#"image_url"];
NSString * image2 = [[tempArray objectAtIndex:0]objectForKey:#"user_image"];
dispatch_queue_t imageQueue = dispatch_queue_create("Image Queue", NULL);
dispatch_async(imageQueue, ^{
NSURL *url = [NSURL URLWithString:image1];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
NSURL *url1 = [NSURL URLWithString:image2];
NSData *imageData1 = [NSData dataWithContentsOfURL:url1];
UIImage *image11 = [UIImage imageWithData:imageData1];
dispatch_async(dispatch_get_main_queue(), ^{
[user_image setImage:image];
[self.tag_user_img setImage:image11];
});
});
check image view is empty or not using
if(CGSizeEqualToSize(imageView.image.size, CGSizeZero))
{
show alert here....
}
dispatch_async(dispatch_get_main_queue(), ^{
if(!image)
{
// show your message
}
else
{
[user_image setImage:image];
}
});
In my app, Im using an API to get images from a server. Using the below code, it gets the images in order of size to replace themselves with better quality.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *imageURL = [NSURL URLWithString: [NSString stringWithFormat:#"%#", [[[self.information objectForKey:#"images"]objectForKey:#"normal"] description]]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
self.shotImage = [UIImage imageWithData:imageData];
self.image.image = self.shotImage;
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *imageURL = [NSURL URLWithString: [NSString stringWithFormat:#"%#", [[[self.information objectForKey:#"images"]objectForKey:#"hidpi"] description]]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:imageData];
self.image.image = self.shotImage;
});
Also, if you press on the image it then takes you too another ViewController where the image is fullscreen.
However, even if I wait for the high quality one to load before I tap on it, in the PrepareForSegue Method, it still only passes the original low quality version.
See code below from PrepareForSegue
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
FullSizeImageViewController *vc = [segue destinationViewController];
UIImage *img = [[UIImage alloc] init];
img = self.shotImage;
vc.shotImage = img;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *imageURL = [NSURL URLWithString: [NSString stringWithFormat:#"%#", [[[self.information objectForKey:#"images"]objectForKey:#"hidpi"] description]]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:imageData];
self.image.image = self.shotImage;
});
In this code looks like you miss this line:
self.shotImage = [UIImage imageWithData:imageData];
I'm having difficulties assigning the value of an image to an image view image. I've tested that the source image has a value at the time of assignment to the image view image, and indeed it does. However, the assignment just does't appear to happen, and the image view image remains null. Can anyone help me with what I'm doing wrong?
Thanks.
- (void)viewDidLoad {
[super viewDidLoad];
self.scrollView.contentSize = self.imageView.image.size;
self.scrollView.delegate = self;
self.scrollView.minimumZoomScale = 1.0;
self.scrollView.maximumZoomScale = 100.0;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSString *imageName = #"http://my.domain.com/image.png";
UIImage *img = [[UIImage alloc] init];
NSURL *imageURL = [NSURL URLWithString:imageName];
[self.activityIndicator startAnimating];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
img = [UIImage imageWithData:imageData];
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = img;//img HAS a value, but self.imageView.image does NOT receive it
[self.activityIndicator stopAnimating];
[self.imageView setNeedsDisplay];
});
});
}
I think problem with UIImage. where you alloc object of UIImage. Try This :
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
[self.activityIndicator startAnimating];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://techbeasts.com/wp-content/uploads/2014/04/Wallpaper-HD.jpg"]]; // Your image URL. First check it in browser image is there.
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = [UIImage imageWithData:imageData];//img HAS a value, but self.imageView.image does NOT receive it
[self.activityIndicator stopAnimating];
[self.imageView setNeedsDisplay];
});
});
The problem is that, you have to use dispatch_async and dispatch_sync pair. So the the inner block will wait till the outer block completes its execution and downloads image.
dispatch_async(queue, ^{
NSString *imageName = #"http://my.domain.com/image.png";
UIImage *img = [[UIImage alloc] init];
NSURL *imageURL = [NSURL URLWithString:imageName];
[self.activityIndicator startAnimating];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
img = [UIImage imageWithData:imageData];
dispatch_sync(dispatch_get_main_queue(), ^{
self.imageView.image = img;//img HAS a value, but self.imageView.image does NOT receive it
[self.activityIndicator stopAnimating];
[self.imageView setNeedsDisplay];
});
});
Then make sure the imageView is allocated with either by programmatically or using IB.
The outlet had not properly connected. Reconnecting it solved the issue.