Load many gif to UICollectionView - ios

I am facing problem when I download gif images and add in to collection view.
gif images are download well, but when i try to scroll very very quickly it's crash
Please help. I have two solutions
/*
cellForGif.layer.borderColor = [[UIColor colorWithRed:54.0/255.f green:56.0/255.f blue:67.0/255.f alpha:1.0]CGColor];
cellForGif.layer.borderWidth = 0.7;
FLAnimatedImage __block *gifImage = nil;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
gifImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"%li", (long)indexPath.row] ofType:#"gif"]]];
dispatch_async(dispatch_get_main_queue(), ^{
cellForGif.gifImage.animatedImage = gifImage;
cellForGif.linkOnGif = [self.linksArrayOnGifs objectAtIndex:indexPath.row];
//gifImage = nil;
});
});
return cellForGif;
*/
cellForGif.layer.borderColor = [[UIColor colorWithRed:54.0/255.f green:56.0/255.f blue:67.0/255.f alpha:1.0]CGColor];
cellForGif.layer.borderWidth = 0.7;
FLAnimatedImage *gifImage = nil;
gifImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"%li", (long)indexPath.row] ofType:#"gif"]]];
cellForGif.gifImage.animatedImage = gifImage;
cellForGif.linkOnGif = [self.linksArrayOnGifs objectAtIndex:indexPath.row];
//gifImage = nil;
return cellForGif;

You need to change your approach.
You have to load all images before going to set them in UICollectionViewCell.
Let's say create an NSArray contains all gif images. After images are loaded successfully then set them to the cell.
By observing straight from your code, I see that you load an image from the main bundle in cellForItemAtIndexPath method. So it is obvious that it will take some time very little (nano sec). But that is also considered large when there are a large amount of data in a cell.
And it is possible that line
[NSData dataWithContentsOfFile:[[NSBundle mainBundle]
will return nil when you scroll very very quickly.
Add a comment if it is still not clear.
EDIT:
Loading images in the background will not affect UI and scrolling will be smoother.
Also, put try-catch block in that method to check what you have missed.
cellForGif.layer.borderColor = [[UIColor colorWithRed:54.0/255.f green:56.0/255.f blue:67.0/255.f alpha:1.0]CGColor];
cellForGif.layer.borderWidth = 0.7;
#try {
FLAnimatedImage __block *gifImage = nil;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
gifImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"%li", (long)indexPath.row] ofType:#"gif"]]];
dispatch_async(dispatch_get_main_queue(), ^{
cellForGif.gifImage.animatedImage = gifImage;
cellForGif.linkOnGif = [self.linksArrayOnGifs objectAtIndex:indexPath.row];
//gifImage = nil;
});
});
}
#catch (NSException *exception) {
NSLog(#"Exception :%#",exception.debugDescription);
}

swift extension based on Kampai's answer:
extension UIImageView {
func setGif(_ url: String) {
DispatchQueue.global(qos: .default).async {
do {
let gifData = try Data(contentsOf: URL(string: url) ?? URL(fileURLWithPath: ""))
DispatchQueue.main.async {
self.image = UIImage.sd_image(withGIFData: gifData)
}
} catch let error {
DispatchQueue.main.async {
self.image = UIImage(named: "defaultImg")// set default image here
}
print(error)
}
}
}
}
use it as:
cell.imageGif.setGif("image url")

Related

Connection interrupted error when loading image from disk using SDWebImage in iOS

I get this error when loading multi images from disk. I used SDWebImage to load image but still get this error then app crashed. I dont know what this error is. Pls give me adivse. Thanks much.
Communications error: <OS_xpc_error: <error: 0x322eb614> { count = 1, contents =
"XPCErrorDescription" => <string: 0x322eb86c> { length = 22, contents = "Connection interrupted" }
}>
Here is the code I used to create the list thumbnail for scrollview
-(void) createPropertyThumbnails:(NSArray*) array
{
int xCoord=10;
int yCoord=7;
int buffer = 7;
for (int pos = 0; pos < array.count; pos++) {
// CREATE VIEW CONTAINS UIIMAGEVIEW HERE
ThumbnailView* thumbnaiCell = [[humbnailView alloc] initWithFrame:CGRectMake(xCoord, yCoord,THUMBNAIL_WIDTH , THUMBNAIL_HEIGHT) andURLImage:array[pos]];
thumbnaiCell.delegate = self;
//set selected for 1st item
if(pos ==0)
{
_selectedCell =thumbnaiCell;
[_selectedCell setSelectedCell:YES];
}
xCoord = xCoord + THUMBNAIL_WIDTH + buffer;
[self.thumbnailScrollView addSubview:thumbnaiCell];
}
[self.thumbnailScrollView setContentSize:CGSizeMake(xCoord, self.thumbnailScrollView.frame.size.height)];
}
- (id)initWithFrame:(CGRect)frame andURLImage:(NSString*) url
{
self = [super initWithFrame:frame];
if (self) {
[self setupProjectThumbnail:frame andURLImage:url];
}
return self;
}
-(void) setupProjectThumbnail:(CGRect) frame andURLImage:(NSString*) url
{
//create load image from cache
NSString *filePath = [[NSBundle mainBundle] pathForResource:url ofType:nil];
NSURL *ImageURL = [NSURL fileURLWithPath:filePath];
if (ImageURL) {
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadImageWithURL:ImageURL
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
// progression tracking code
}
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (image) {
// do something with image
// imageview
UIImageView* imageview = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, frame.size.width, frame.size.height)];
imageview.contentMode = UIViewContentModeScaleToFill;
[self addSubview:imageview];
}
}];
}
}

Refreshing UITableViewCell after fetching image data by dispatch_async

I'm writing app that shows TableView with entries that contains images.
I'm trying to fetch image by executing this line of code inside cellForRowAtIndexPath method:
cell.detailTextLabel.text = [artistData objectForKey:generesKey];
dispatch_async(backgroundQueue, ^{
NSURL *url_img = [NSURL URLWithString:[artistData objectForKey:pictureKey]];
NSData* data = [NSData dataWithContentsOfURL:
url_img];
cell.imageView.image = [UIImage imageWithData:data];
[self performSelectorOnMainThread:#selector(refreshCell:) withObject:cell waitUntilDone:YES];
});
After setting image I perform selector that contains:
-(void)refreshCell:(UITableViewCell*)cell{
[cell setNeedsDisplay];
[self.view setNeedsDisplay];
[self.tableViewOutlet setNeedsDisplay];
}
And image is not shown but when I click on cell or scroll entire list, images are shown. Why my View is not refreshing? Did I missed something?
It should be enough to call setNeedsLayout() on a cell.
In swift 4 it looks like this:
DispatchQueue.global().async {
let data = try? Data(contentsOf: URL(string: imageUrl)!)
DispatchQueue.main.async {
cell.imageView?.image = UIImage(data: data!)
cell.setNeedsLayout()
}
}
You'll could always reload the cell by calling [self.tableView reloadRowsAtIndexPaths#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
In order to prevent the infinite loop once you successfully download an image you'll want to cache the results. How long you cache is up to you.
NSCache *imageCache = [[NSCache alloc] init];
imageCache.name = #"My Image Cache";
UIImage *image = [imageCache objectForKey:url_img];
if (image) {
cell.imageView.image = image;
} else {
// Do your dispatch async to fetch the image.
// Once you get the image do
[imageCache setObject:[UIImage imageWithData:data] forKey:url_img];
}
You'll want the imageCache to be a property at the ViewController level. Don't create one each time incellForRowAtIndexPath
It might be related to interacting with the UI from a background queue. Try this:
dispatch_async(backgroundQueue, ^{
NSURL *url_img = [NSURL URLWithString:[artistData objectForKey:pictureKey]];
NSData* data = [NSData dataWithContentsOfURL:url_img];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = [UIImage imageWithData:data];
});
});

Multitasking with download images in iOS sdk

I have a UItableView that is listing number of outlets respectively, each outlet has a logo image. I save those images locally on iPhone, so if any image is found on iPhone it will fetch from there if not then it will send a service call and fetch data. It is working accordingly but when I scroll down on tableview it gets hanged at certain point when the image is being downloaded, as it is downloaded it works fine again. Is there any solution where I can perform this multitasking of downloading images along with representing them either from service call or fetching it locally.
Here is my code..
// downloading images of outlets locally
NSLog(#"Downloading...");
NSString *imageLink = [NSString stringWithFormat:#"http://cf.abc.pk/outlets/l/%#",outs.logo];
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageLink]]];
NSLog(#"%f,%f",image.size.width,image.size.height);
NSString *docDir =[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSLog(#"%#",docDir);
NSLog(#"saving png");
NSString *pngFilePath = [NSString stringWithFormat:#"%#/%d.png",docDir,[[arrOutletIds objectAtIndex:webserviceCall] intValue]];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:pngFilePath];
if(!fileExists)
{
NSData *data1 = [NSData dataWithData:UIImagePNGRepresentation(image)];
[data1 writeToFile:pngFilePath atomically:YES];
}
Let's try:
//When you download something, execute in background thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
^{
//download some thing
dispatch_async( dispatch_get_main_queue(), ^{
// when download finish execute in main thread to update data
// this function should be called in response of downloading
});
});
Hope this helps you.
Download image in background will solve your problem here
Try this.
if(!fileExists)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data1 = [NSData dataWithData:UIImagePNGRepresentation(image)];
[data1 writeToFile:pngFilePath atomically:YES];
});
}
You can use this code which integrates ImageCache to download async and cache images. This part of code shows downloading images in UITableView. Or you can check out this project on GitHub SDImageCache
if ([[ImageCache sharedImageCache] DoesExist:photoURL] == true)
{
image = [[ImageCache sharedImageCache] GetImage:photoURL];
cell.imageView.image = image;
}
else
{
cell.imageView.image = [UIImage imageNamed:#"PlaceHolder"];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) {
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:photoURL]];
UIImage* image = [[UIImage alloc] initWithData:imageData];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
if (cell.tag == indexPath.section) {
cell.imageView.image = image;
[cell setNeedsLayout];
[[ImageCache sharedImageCache] AddImage:image imageURL:photoURL];
}
});
}
});
}
Import these files to your project
ImageCache.h
#import <Foundation/Foundation.h>
#interface ImageCache : NSObject
#property (nonatomic, retain) NSCache *imgCache;
#pragma mark - Methods
+ (ImageCache*)sharedImageCache;
- (void) AddImage:(UIImage *)image imageURL:(NSString *)imageURL;
- (UIImage*) GetImage:(NSString *)imageURL;
- (BOOL) DoesExist:(NSString *)imageURL;
#end
ImageCache.m
#import "ImageCache.h"
#implementation ImageCache
#synthesize imgCache;
#pragma mark - Methods
static ImageCache* sharedImageCache = nil;
+(ImageCache*)sharedImageCache
{
#synchronized([ImageCache class])
{
if (!sharedImageCache)
sharedImageCache= [[self alloc] init];
return sharedImageCache;
}
return nil;
}
+(id)alloc
{
#synchronized([ImageCache class])
{
NSAssert(sharedImageCache == nil, #"Attempted to allocate a second instance of a singleton.");
sharedImageCache = [super alloc];
return sharedImageCache;
}
return nil;
}
-(id)init
{
self = [super init];
if (self != nil)
{
imgCache = [[NSCache alloc] init];
}
return self;
}
- (void) AddImage:(UIImage *)image imageURL:(NSString *)imageURL
{
if (image==nil) {
}
else
{
[imgCache setObject:image forKey:imageURL];
}
}
- (NSString*) GetImage:(NSString *)imageURL
{
return [imgCache objectForKey:imageURL];
}
- (BOOL) DoesExist:(NSString *)imageURL
{
if ([imgCache objectForKey:imageURL] == nil)
{
return false;
}
return true;
}
#end

Need to see which UIImageView is empty and place an image there

Here's my problem. I have 3 UIImageViews that are empty by default, placed on a UIView that are is hidden at first.
And they are supposed to be filled one after another when a user selects something.
I know it sounds confusing, but I think my code below will clear things out:
- (IBAction)selectButtonPressed:(id)sender {
if ([labelText.text isEqualToString:#"some text"]) {
NSString *filePath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"someImage"] ofType:#"png"];
UIImage * image = (UIImage * )[UIImage imageWithContentsOfFile:filePath];
if (firstImage.image == nil) {
firstImage.image = image;
imagesView.hidden = YES;
firstImage.hidden = NO;
} // places it in the first Image View if it's empty ...
else if (secondImage.image == nil) {
secondImage.image = image;
imagesView.hidden = YES;
secondImage.hidden = NO;
} // ... or in the second one if empty
else {
thirdImage.image = image;
imagesView.hidden = YES;
thirdImage.hidden = NO; // ... else in the third one
}
[filePath release];
[image release];
}
// then does the same thing depending on the users selection.
else if ([labelText.text isEqualToString:#"some other text"]) {
NSString *filePath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"someOtherImage"] ofType:#"png"];
UIImage * image = (UIImage * )[UIImage imageWithContentsOfFile:filePath];
if (firstImage.image == nil) {
firstImage.image = image;
imagesView.hidden = YES;
firstImage.hidden = NO;
}
else if (secondImage.image == nil) {
secondImage.image = image;
imagesView.hidden = YES;
secondImage.hidden = NO;
}
else {
thirdImage.image = image;
imagesView.hidden = YES;
thirdImage.hidden = NO;
}
[filePath release];
[image release];
}
// and same thing below if none of the above.
else {
NSString *filePath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"yet another text"] ofType:#"png"];
UIImage * image = (UIImage * )[UIImage imageWithContentsOfFile:filePath];
if (firstImage.image == nil) {
firstImage.image = image;
imagesView.hidden = YES;
firstImage.hidden = NO;
}
else if (secondImage.image == nil) {
secondImage.image = image;
imagesView.hidden = YES;
secondImage.hidden = NO;
}
else {
thirdImage.image = image;
imagesView.hidden = YES;
thirdImage.hidden = NO;
}
[filePath release];
[image release];
} // Code for checking which Image View is empty and place the image there.
}
So, it launches and works fine, when I do the first selection. But for the 2nd one it says EXC_BAD_ACCESS and crashes. Sometimes it works for the first 2 selections and for the third one it crashes.
Please let me know if it's confusing, I'll try to explain my intent as clear as I can.
Thanks a lot
UPDATE: Well, I think it's true that an answer appears when you ask the right question.
Everything worked well, after I've used firstImage.image = [UIImage imageNamed:#"someImage"]; instead of the whole NSBundle method. Code became cleaner and works like a charm.
However I'm not sure if there are any downsides to this. So if you guys can warn me of anything, I'm all opened to that.
Thanks again.
Your problem probably related to your memory management. Did you use property for those imageview? Also, you don't need to release filePath and image because you did not use the alloc, etc. method to initialize it.

Rendering the layer of a UIWebView that contains an SVG file

I am attempting to use this method I've constructed to save the layer of a UIWebView as an image. That is working but the SVG I have loaded into the UIView is not getting rendered in the image, only the background of the UIWebView.
+ (BOOL)imageFromSVGinWebView:(UIView *)theView usePNG:(BOOL)usePNG; {
UIGraphicsBeginImageContext(theView.frame.size);
[theView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *layerImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData *layerIMGData = nil;
NSString *pathComponent = nil;
if (usePNG) {
layerIMGData = UIImagePNGRepresentation(layerImage);
pathComponent = #"png";
}else {
layerIMGData = UIImageJPEGRepresentation(layerImage,1.0f);
pathComponent = #"jpg";
}
NSString *appFile = [[CCFileManagement conversionsDirectory] stringByAppendingPathComponent:String(#"img.%#",pathComponent)];
NSError *error = nil;
BOOL returnBOOL = [layerIMGData writeToFile:appFile options:NSDataWritingAtomic error:&error];
if(error != nil)
ILogPlus(#"%#",error);
return returnBOOL;
}
In summary, I've loaded an SVG into a UIWebView, saved that UIWebView as an image, opened the image and the image does not have the SVG in it. I have done this with other views successfully, something is different about the SVG or UIWebView.
UPDATE:
If I add a delay to make the app wait a few seconds to execute the image saving code then it works. Putting that code in webViewDidFinishLoad does NOT work. Obviously webViewDidFinishLoad doesn't count SVGs.
I have found that if you add a .1 second delay it will work
- (void)webViewDidFinishLoad:(UIWebView *)webView; {
[self performSelector:#selector(postLoadActions:) withObject:webView afterDelay:0.1f];
}
- (void)postLoadActions:(UIWebView *)webView; {
if (![CCConversion imageFromSVGinWebView:webView usePNG:YES]) {
ILogPlus(#"Didn't work");
}
NSArray *array = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[CCFileManagement conversionsDirectory] error:NULL];
ILogPlus(#"%#",array);
//[webView removeFromSuperview];
[webView release];
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:[[CCFileManagement conversionsDirectory] stringByAppendingPathComponent:#"img.png"]]];
imageView.frame = CGRectMake(90, 90, imageView.frame.size.width, imageView.frame.size.height);
imageView.backgroundColor = [UIColor clearColor];
[self.view performSelector:#selector(addSubview:) withObject:imageView afterDelay:2.0f];
[imageView release];
}

Resources