In my application i want load the gif images in my view and also controls stop and continue animating.
Kindly follow the below tutorial that may help you.
http://anand3777.blogspot.in/2014/07/gif-image-loading.html
Step 1:
Download library from below url and add it on your project.
https://drive.google.com/folderview?id=0B5JC34Lt79ctamtOVTV6SHhzOVU&usp=sharing
Step 2:
Import the file on your "viewController.h" like below.
//gifImage//
#import "SCGIFImageView.h"
//gifImage//
Step 3:
Create object like given below on your "viewController.h".
//gifImage//
IBOutlet SCGIFImageView* _gifImageView;
IBOutlet UIButton* _button;
//gifImage//
Step 4:
Create ibaction on your "viewController".
//gifImage//
- (IBAction)controlAnimate:(id)sender;
//gifImage//
Step 5:
Add the following code were you want to use gif image
//gifImage//
//load url gif image
NSURL *imageURL = [NSURL URLWithString:#"http://google.co.in/anim.gif"];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
/*
//load local gif image
NSString* filePath = [[NSBundle mainBundle] pathForResource:#"1.gif" ofType:nil];
NSData* imageData = [NSData dataWithContentsOfFile:filePath];
*/
_gifImageView = [[SCGIFImageView alloc] initWithFrame:CGRectMake(225, 70, 75, 75)] ;
[_gifImageView setData:imageData];
[self.view addSubview:_gifImageView];
//gifImage//
Step 6:
Add the following code for stop/start animating
//gifImage//
- (IBAction)controlAnimate:(id)sender{
_gifImageView.animating = !_gifImageView.animating;
if (_gifImageView.animating) {
[_button setTitle:#"Pause" forState:UIControlStateNormal];
} else {
[_button setTitle:#"Continue" forState:UIControlStateNormal];
}
}
//gifImage//
[1]: https://drive.google.com/folderview?id=0B5JC34Lt79ctamtOVTV6SHhzOVU&usp=sharing
Use Latest GIF Class from https://github.com/Flipboard/FLAnimatedImage
It is easy to use and memory friendly.
In your code, #import "FLAnimatedImage.h", create an image from an animated GIF, and setup the image view to display it:
FLAnimatedImage *image = [FLAnimatedImage animatedImageWithGIFData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"https://upload.wikimedia.org/wikipedia/commons/2/2c/Rotating_earth_%28large%29.gif"]]];
FLAnimatedImageView *imageView = [[FLAnimatedImageView alloc] init];
imageView.animatedImage = image;
imageView.frame = CGRectMake(0.0, 0.0, 100.0, 100.0); //As your Wish you can set frame
[self.view addSubview:imageView];
or If you are using Bundle gif file then
NSURL *url = [[NSBundle mainBundle] URLForResource:#"loading_01" withExtension:#"gif"];
FLAnimatedImage *image = [FLAnimatedImage animatedImageWithGIFData:[NSData dataWithContentsOfURL:url]];
FLAnimatedImageView *imageView = [[FLAnimatedImageView alloc] init];
imageView.animatedImage = image;
imageView.frame = CGRectMake(0.0, 0.0, 100.0, 100.0); //As your Wish you can set frame
[self.view addSubview:imageView];
Related
I'm using SDWebImage to download and cache images in an UIImageView asynchronously but I'm facing some problems.
In my project I create a pin marker which has a dialogue box that have UIImageView in it.
The problem that I face is the image from url didn't replace place holder image after it's finished download. The first click on pin marker will always display the place holder image.
The image from url will only display after I click pin marker for the second time.
I want to display image from the url in the first click what should I do?
here is my code
-(UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(GMSMarker *)marker{
PlaceData *currentMarker = [self findDataByMarker:marker];
UIView *dialogView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 261, 95)];
// Create background image
UIImageView *dialogBackground = [[UIImageView alloc] initWithImage: [UIImage imageNamed:#"MapDialog.png"]];
[dialogView addSubview:dialogBackground];
UIImageView * thumbnail = [[UIImageView alloc] init];
[thumbnail sd_setImageWithURL:[NSURL URLWithString:currentMarker.thumbnail] placeholderImage:[UIImage imageNamed:#"placeholder.png"] options:SDWebImageRefreshCached];
NSLog(#"URL %#", currentMarker.thumbnail);
NSLog(#"thumbnail %#",thumbnail);
CGRect tmpFrame = thumbnail.frame;
tmpFrame.origin.x = 15;
tmpFrame.origin.y = 14;
tmpFrame.size.width = 55;
tmpFrame.size.height = 55;
thumbnail.frame = tmpFrame;
thumbnail.contentMode = UIViewContentModeScaleToFill;
[dialogView addSubview:thumbnail];
return dialogView;
PS. Please help me solve this problem and sorry for my english.
[.retryFailed, .refreshCached] helped solve my issue with this.
imageView.sd_setImage(with: picnicThumbnail, maxImageSize: 100000, placeholderImage: placeholderImage, options: [.retryFailed, .refreshCached], completion : nil)
Please try with this code
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[thumbnail sd_setImageWithURL:[NSURL URLWithString:currentMarker.thumbnail] placeholderImage:[UIImage imageNamed:#"placeholder.png"] options:SDWebImageRefreshCached];
});
In my iOS app, I have a UICollectionView where each cell contains an image. To prevent the view from taking a long time to load, I load each with a blank image and title before loading the image contents in a background task.
I logged which images are getting loaded through in the background async task, and it seems like the images of cells off screen get loaded first, followed by the cells at the top of the screen. This makes the app seem unresponsive, and I'd rather have the cells at the top take priority in terms of loading:
I also notice that once I start scrolling, the images in the cells suddenly start appearing, but they take much longer to appear on their own. Can anyone suggest strategies to control the ordering that UICollectionCells load in?
Here is my code:
Iterate over projects and add imageViews to an NSMutableArray projectContainers, which then gets turned into cells
for (NSDictionary *currentProject in projects)
{
// data entry
[projectIDs addObject: [currentProject objectForKey:#"id"]];
NSString *projectTitle = [currentProject objectForKey:#"title"];
id delegate = [[UIApplication sharedApplication] delegate];
self.managedObjectContext = [delegate managedObjectContext];
CustomLabel *cellLabel=[[CustomLabel alloc]init];
cellLabel.text = trimmedProjectTitle;
[titles addObject:projectTitle];
CGSize maxLabelSize = CGSizeMake(cellWidth,100);
CustomLabel *titleLabel = [[CustomLabel alloc]init];
// titleLabel styling
titleLabel.backgroundColor = [[UIColor blackColor]colorWithAlphaComponent:0.5f];
titleLabel.textColor =[UIColor whiteColor];
[titleLabel setFont: [UIFont fontWithName: #"HelveticaNeue" size:12]];
titleLabel.text = trimmedProjectTitle;
CGSize expectedLabelSize = [titleLabel.text sizeWithFont:titleLabel.font constrainedToSize:maxLabelSize lineBreakMode:NSLineBreakByWordWrapping];
CGRect labelFrame = (CGRectMake(0, 0, cellWidth, 0));
labelFrame.origin.x = 0;
labelFrame.origin.y = screenWidth/2 - 80 - expectedLabelSize.height;
labelFrame.size.height = expectedLabelSize.height+10;
titleLabel.frame = labelFrame;
// add placeholder image with textlabel
UIImageView *imagePreview = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, cellWidth, cellHeight)];
imagePreview.contentMode= UIViewContentModeScaleAspectFill;
imagePreview.clipsToBounds = YES;
[imagePreview setImage:[UIImage imageNamed:#"blank.png"]];
[imagePreview addSubview:titleLabel];
[imagePreview.subviews[0] setClipsToBounds:YES];
[projectContainers addObject: imagePreview];
// add project thumbnail images in async
dispatch_async(bgQueue, ^{
NSDictionary *imagePath = [currentProject objectForKey:#"image_path"];
NSString *imageUrlString = [imagePath objectForKey: #"preview"];
NSURL *imageUrl = [NSURL URLWithString: imageUrlString];
NSData *imageData = [[NSData alloc] initWithContentsOfURL:(imageUrl)];
UIImage *image = [[UIImage alloc] initWithData:(imageData)];
if(image){
NSLog(#"project with image: %#", projectTitle);
[imagePreview setImage: image];
}
BOOL *builtVal = [[currentProject objectForKey:#"built"]boolValue];
if(builtVal){
UIImageView *builtBanner =[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"built_icon.png"]];
builtBanner.frame = CGRectMake(screenWidth/2 -80, 0, 50, 50);
[imagePreview addSubview: builtBanner];
}
});
}
renders cells using the NSMutableArray projectContainers:
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
// NSLog(#"cellForItemAtIndexPath");
static NSString *identifier = #"NewCell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
if(!reloadProjects){
UIImageView *preview = (UIImageView*) [cell.contentView viewWithTag:[[projectIDs objectAtIndex:indexPath.row]intValue]];
UIImageView *previewContent = [projectContainers objectAtIndex:indexPath.row];
// NSLog(#"fetching image tag %d", [[projectIDs objectAtIndex:indexPath.row]intValue]);
if (!preview)
{
previewContent.tag = [[projectIDs objectAtIndex:indexPath.row]intValue];
// NSLog(#"creating previewContent %li", (long) previewContent.tag);
[cell addSubview: previewContent];
}
[self.collectionView setBackgroundColor:collectionGrey];
cell.contentView.layer.backgroundColor = [UIColor whiteColor].CGColor;
return cell;
}
return cell;
}
EDIT: Working Solution
Thanks to rob mayoff for helping me come out with a solution. This is what I ended up doing, which loads the images much faster:
// add project thumbnail images in async
dispatch_async(imageQueue, ^{
NSDictionary *imagePath = [currentProject objectForKey:#"image_path"];
NSString *imageUrlString = [imagePath objectForKey: #"preview"];
NSURL *imageUrl = [NSURL URLWithString: imageUrlString];
NSData *imageData = [[NSData alloc] initWithContentsOfURL:(imageUrl)];
UIImage *image = [[UIImage alloc] initWithData:(imageData)];
if(image){
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"project with image: %#", projectTitle);
[imagePreview setImage: image];
});
}
BOOL *builtVal = [[currentProject objectForKey:#"built"]boolValue];
if(builtVal){
UIImageView *builtBanner =[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"built_icon.png"]];
builtBanner.frame = CGRectMake(screenWidth/2 -80, 0, 50, 50);
dispatch_async(dispatch_get_main_queue(), ^{
[imagePreview addSubview: builtBanner];
});
}
});
There are several things that code be improved in your code, but your chief complaint (“once I start scrolling, the images in the cells suddenly start appearing, but they take much longer to appear on their own”) is because you violated the commandment:
Thou shalt only access
thy view hierarchy
from the main thread.
Look at your code:
dispatch_async(bgQueue, ^{
...
[imagePreview addSubview: builtBanner];
You're manipulating the view hierarchy from a background thread. This is not allowed. For example, see the note at the bottom of this page, or the “Threading Considerations” in the UIView Class Reference.
You need to dispatch back to the main thread to update the view hierarchy.
Watch the Session 211 - Building Concurrent User Interfaces on iOS video from WWDC 2012. It talks in depth about how to do what you're trying to do, efficiently. See also this answer.
I created a custom class called Slot and it is a subclass of UIView. In the slot class, the following function is called:
- (NSString*)assignItem:(Item*)item {
NSString *message;
if (item.slotSize + currentCapacity > slotSize) {
message = #"Sorry this item will not fit.";
} else {
//uiimageview stuff
UIImage *image = [[UIImage alloc] initWithContentsOfFile:item.imageName];
UIImageView *iv = [[UIImageView alloc] initWithImage:image];
[self addSubview:iv];
[items addObject:item];
currentCapacity = currentCapacity + item.slotSize;
message = #"Success";
}
NSLog(#"%#",message);
return message;
}
What happens is an Item (another custom class, subclass of NSObject) is passed to the Slot a UIImageView is created from the item from a string to an image in the bundle and it is added to the subview. However, the image isn't showing. The Success message is showing so I know its getting in there. Is there another way to add a subview from the subclass or am I just doing it all wrong?
UIImage *image = [[UIImage alloc] initWithContentsOfFile:item.imageName];
This is not the correct way to instantiate an image that you have in the bundle. You can either use imageNamed: like this,
UIImage *image = [UIImage imageNamed:item.imageName];
Or, you can get the image with initWithContentsOfFile like this,
NSString *imagePath = [[NSBundle mainBundle] pathForResource:item.imageName ofType:#"JPG"]; // replace JPG with whatever is appropriate for your image.
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
imageNamed: will cache the images, the other way doesn't.
I have an image (a .png file) that I want to place in an ImageView in a ViewController. I use the following code but the simulator gives me a blank white view without the image. The .png file is in the same directory as the ViewController files. Here is the code:
#implementation ViewController
{
NSArray *_pArray;
UIImage *_image;
UIImageView *_imageView;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_image = [[UIImage alloc] initWithContentsOfFile:#"TM-1P2.png"];
_imageView = [[UIImageView alloc]initWithFrame:self.view.bounds];
[_imageView setImage:_image];
[_imageView setContentMode:UIViewContentModeScaleAspectFit];
[self.view addSubview:_imageView];
}
If you examine at _image (either NSLog or in the debugger), it probably is nil. With initWithContentsOfFile you should specify the entire path, for example:
NSString *path = [[NSBundle mainBundle] pathForResource:#"TM-1P2" ofType:#"png"];
_image = [[UIImage alloc] initWithContentsOfFile:path];
Alternatively, you can use the following, which automatically looks for the image in the bundle:
_image = [UIImage imageNamed:#"TM-1P2.png"];
This latter syntax, imageNamed, caches the image (i.e. will keep it in memory even if you dismiss the view controller). That's great if you have to use the same image again and again throughout the app (because it won't have to reload it every time), but if you only use it once, you might not want to use imageNamed. As the imageNamed documentation says:
If you have an image file that will only be displayed once and wish to ensure that it does not get added to the system’s cache, you should instead create your image using imageWithContentsOfFile:. This will keep your single-use image out of the system image cache, potentially improving the memory use characteristics of your app.
Note, both of these assume that you've successfully added this image to your bundle.
If, on the other hand, the image is in your Documents folder, you could load it like so:
NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *path = [documentsPath stringByAppendingPathComponent:#"TM-1P2.png"];
_image = [[UIImage alloc] initWithContentsOfFile:path];
Finally, note that the iOS devices are case sensitive (generally the simulator is not), so make sure you have your capitalization correct.
Unrelated to your question, those variables in between the braces probably should not be defined in the #implementation, but rather you should put them in a #interface. For example, you could put them in your .h file, or better, you can put them in a private class extension in your .m file, right before the #implementation:
#interface ViewController ()
{
NSArray *_pArray;
UIImage *_image;
UIImageView *_imageView;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"TM-1P2" ofType:#"png"];
_image = [[UIImage alloc] initWithContentsOfFile:path];
_imageView = [[UIImageView alloc]initWithFrame:self.view.bounds];
[_imageView setImage:_image];
[_imageView setContentMode:UIViewContentModeScaleAspectFit];
[self.view addSubview:_imageView];
}
// ...
#end
Have you stepped through in the debugger?
I would be interested to see if _image is non-nil - and the most likely reason for it being nil is that you have not added it to your project.
If you have your image named as "TM-1P2.png" in your bundle, you can simply do the following:
_image = [UIImage imageNamed:#"TM-1P2.png"];
- (void)viewDidLoad {
[super viewDidLoad];
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.layer.frame.size.width, self.view.layer.frame.size.height)];
imgView.image = [UIImage imageNamed:#"emptyCart.jpeg"];
imgView.backgroundColor = [UIColor whiteColor];
imgView.contentMode = UIViewContentModeCenter;
[self.view addSubview: imgView];
}
I'm trying to load images in the background using gcd. My first attempt didn't work:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
// create UI Image, add to subview
});
});
commenting out the background queue code and leaving just the main queue dispatch block didn't work:
dispatch_async(dispatch_get_main_queue(), ^{
// create UI Image, add to subview
});
Is there some trick to doing ui stuff inside a gcd block?
In response to mattjgalloway's comment, I'm simply trying to load a big image in a background thread. Here's the full code that I tried originally:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0), ^{
UIImage* img = [[UIImage alloc] initWithContentsOfFile: path];
dispatch_async(dispatch_get_main_queue(), ^{
imageView = [[[UIImageView alloc] initWithImage: img] autorelease];
imageView.contentMode = UIViewContentModeScaleAspectFill;
CGRect photoFrame = self.frame;
imageView.frame = photoFrame;
[self addSubview:imageView];
});
});
I simplified it so that I was running everything inside the main queue, but even then it didn't work. I figure if I can't get the whole thing to work in the main queue, no way the background queue stuff will work.
================ UPDATE ==============
I tried the above technique (gcd and all) in a brand new project and it does indeed work as expected. Just not in my current project. So I'll have to do some slow, painful process of elimination work to figure out what's going wrong. I'm using this background loading to display images in a uiscrollview. Turns out bits of the images do sometimes show up, but not always. I'll get to the bottom of this....
================ UPDATE ==============
Looks like the issue is related to the UIScrollView all of this is inside. I think stuff isn't getting drawn/refreshed when it should
I ran your code. It works with one exception. Also did you mean self.view rather than self?
Case 1:
path is declared at a property.
imageView is declared at an ivar
- (IBAction)buttonImagePressed:(id)sender
{
self.path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"picture1.jpg"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0), ^{
UIImage* img = [[UIImage alloc] initWithContentsOfFile: self.path];
dispatch_async(dispatch_get_main_queue(), ^{
imageView = [[[UIImageView alloc] initWithImage: img] autorelease];
imageView.contentMode = UIViewContentModeScaleAspectFill;
CGRect photoFrame = self.view.frame;
imageView.frame = photoFrame;
[self.view addSubview:imageView];
});
});
}
Case 2:
path and imageView are both ivars. - no property is used.
- (IBAction)buttonImagePressed:(id)sender
{
// will crash if path is defined here
// path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"picture1.jpg"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0), ^{
//if path is defined here then it will work
path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"picture1.jpg"];
UIImage* img = [[UIImage alloc] initWithContentsOfFile:path];
dispatch_async(dispatch_get_main_queue(), ^{
imageView = [[[UIImageView alloc] initWithImage: img] autorelease];
imageView.contentMode = UIViewContentModeScaleAspectFill;
CGRect photoFrame = self.view.frame;
imageView.frame = photoFrame;
[self.view addSubview:imageView];
});
});
}
On case 2, where it crashed, the message in the console said to file a bug at apple.com ...
Try to call setNeedDisplay right after [self addSubview:imageView], by the way most of UIKit isn't thread safe, I don't know about UIImage methods specifically (maybe is ok on iOS4 and higher), but I wouldn't do that. If you want to load images from background better use ImageIO that is thread safe, or Core Graphics functions.
An alternative could be load an NSData object with your image data on a background thread an later call [UIImage alloc]inithWithData: data] on the main thread.
Displaying my image as a background image, rather than a UIImageView added as a subview works. No clue why
#interface PhotoView(){
UIImageView* imageView;
}
#end
#implementation PhotoView
-(id)initWithFrame:(CGRect)frame andPathToPhoto:(NSString*)path andSequenceView:(SequencerView*) sequencerView{
if(self = [super initWithFrame:frame]){
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0), ^{
UIImage* img = [[UIImage alloc] initWithContentsOfFile: path];
dispatch_async(dispatch_get_main_queue(), ^{
self.backgroundColor = [UIColor colorWithPatternImage:img];
});
[img release];
});
}
return self;
}
#end