//AnimatedGif.h
#import <UIKit/UIKit.h>
@interface AnimatedGifFrame : NSObject
{
NSData *data;
NSData *header;
double delay;
int disposalMethod;
CGRect area;
}
@property (nonatomic, copy) NSData *header;
@property (nonatomic, copy) NSData *data;
@property (nonatomic) double delay;
@property (nonatomic) int disposalMethod;
@property (nonatomic) CGRect area;
@end
@interface AnimatedGif : NSObject
{
NSData *GIF_pointer;
NSMutableData *GIF_buffer;
NSMutableData *GIF_screen;
NSMutableData *GIF_global;
NSMutableArray *GIF_frames;
bool busyDecoding;
int GIF_sorted;
int GIF_colorS;
int GIF_colorC;
int GIF_colorF;
int dataPointer;
}
@property bool busyDecoding;
+ (id)gifWithPath:(NSString*)absolutePath;
- (id)initWithPath:(NSString*)absolutePath;
- (void) decodeGIF:(NSData *)GIF_Data;
- (void) GIFReadExtensions;
- (void) GIFReadDescriptor;
- (bool) GIFGetBytes:(int)length;
- (bool) GIFSkipBytes: (int) length;
- (NSData*) getFrameAsDataAtIndex:(int)index;
- (NSArray *)frames;
@end
//AnimatedGif.m
#import “AnimatedGif.h”
@implementation AnimatedGifFrame
@synthesize data, delay, disposalMethod, area, header;
- (void) dealloc
{
[data release];
[header release];
[super dealloc];
}
@end
@implementation AnimatedGif
@synthesize busyDecoding;
+ (id)gifWithPath:(NSString*)absolutePath
{
AnimatedGif* gif = [[AnimatedGif alloc] initWithPath:absolutePath];
return [gif autorelease];
}
- (id) init
{
self = [super init];
if (self) {
}
return self;
}
- (id)initWithPath:(NSString*)absolutePath
{
self = [self init];
if (self) {
[self decodeGIF:[NSData dataWithContentsOfFile:absolutePath]];
}
return self;
}
- (void) dealloc
{
if (GIF_buffer != nil)
{
[GIF_buffer release];
}
if (GIF_screen != nil)
{
[GIF_screen release];
}
if (GIF_global != nil)
{
[GIF_global release];
}
[GIF_frames release];
[super dealloc];
}
//
- (void)decodeGIF:(NSData *)GIFData
{
GIF_pointer = GIFData;
if (GIF_buffer != nil)
{
[GIF_buffer release];
}
if (GIF_global != nil)
{
[GIF_global release];
}
if (GIF_screen != nil)
{
[GIF_screen release];
}
[GIF_frames release];
GIF_buffer = [[NSMutableData alloc] init];
GIF_global = [[NSMutableData alloc] init];
GIF_screen = [[NSMutableData alloc] init];
GIF_frames = [[NSMutableArray alloc] init];
// Reset file counters to 0
dataPointer = 0;
[self GIFSkipBytes: 6]; // GIF89a, throw away
[self GIFGetBytes: 7]; // Logical Screen Descriptor
// Deep copy
[GIF_screen setData: GIF_buffer];
// Copy the read bytes into a local buffer on the stack
// For easy byte access in the following lines.
int length = [GIF_buffer length];
unsigned char aBuffer[length];
[GIF_buffer getBytes:aBuffer length:length];
if (aBuffer[4] & 0×80) GIF_colorF = 1; else GIF_colorF = 0;
if (aBuffer[4] & 0×08) GIF_sorted = 1; else GIF_sorted = 0;
GIF_colorC = (aBuffer[4] & 0×07);
GIF_colorS = 2 << GIF_colorC;
if (GIF_colorF == 1)
{
[self GIFGetBytes: (3 * GIF_colorS)];
// Deep copy
[GIF_global setData:GIF_buffer];
}
unsigned char bBuffer[1];
while ([self GIFGetBytes:1] == YES)
{
[GIF_buffer getBytes:bBuffer length:1];
if (bBuffer[0] == 0x3B)
{ // This is the end
break;
}
switch (bBuffer[0])
{
case 0×21:
// Graphic Control Extension (#n of n)
[self GIFReadExtensions];
break;
case 0x2C:
// Image Descriptor (#n of n)
[self GIFReadDescriptor];
break;
}
}
// clean up stuff
[GIF_buffer release];
GIF_buffer = nil;
[GIF_screen release];
GIF_screen = nil;
[GIF_global release];
GIF_global = nil;
}
//
// Returns a subframe as NSMutableData.
// Returns nil when frame does not exist.
//
// Use this to write a subframe to the filesystems (cache etc);
- (NSData*) getFrameAsDataAtIndex:(int)index
{
if (index < [GIF_frames count])
{
return ((AnimatedGifFrame *)[GIF_frames objectAtIndex:index]).data;
}
else
{
return nil;
}
}
//
// Returns a subframe as an autorelease UIImage.
// Returns nil when frame does not exist.
//
// Use this to put a subframe on your GUI.
- (UIImage*) getFrameAsImageAtIndex:(int)index
{
NSData *frameData = [self getFrameAsDataAtIndex: index];
UIImage *image = nil;
if (frameData != nil)
{
image = [UIImage imageWithData:frameData];
}
return image;
}
//
// This method converts the arrays of GIF data to an animation, counting
// up all the seperate frame delays, and setting that to the total duration
// since the iPhone Cocoa framework does not allow you to set per frame
// delays.
//
// Returns nil when there are no frames present in the GIF, or
// an autorelease UIImageView* with the animation.
- (void)GIFReadExtensions
{
// 21! But we still could have an Application Extension,
// so we want to check for the full signature.
unsigned char cur[1], prev[1];
[self GIFGetBytes:1];
[GIF_buffer getBytes:cur length:1];
while (cur[0] != 0×00)
{
// TODO: Known bug, the sequence F9 04 could occur in the Application Extension, we
//
if (cur[0] == 0×04 && prev[0] == 0xF9)
{
[self GIFGetBytes:5];
AnimatedGifFrame *frame = [[AnimatedGifFrame alloc] init];
unsigned char buffer[5];
[GIF_buffer getBytes:buffer length:5];
frame.disposalMethod = (buffer[0] & 0x1c) >> 2;
//NSLog(@”flags=%x, dm=%x”, (int)(buffer[0]), frame.disposalMethod);
// We save the delays for easy access.
frame.delay = (buffer[1] | buffer[2] << 8);
unsigned char board[8];
board[0] = 0×21;
board[1] = 0xF9;
board[2] = 0×04;
for(int i = 3, a = 0; a < 5; i++, a++)
{
board[i] = buffer[a];
}
frame.header = [NSData dataWithBytes:board length:8];
[GIF_frames addObject:frame];
[frame release];
break;
}
prev[0] = cur[0];
[self GIFGetBytes:1];
[GIF_buffer getBytes:cur length:1];
}
}
- (void) GIFReadDescriptor
{
[self GIFGetBytes:9];
// Deep copy
NSMutableData *GIF_screenTmp = [NSMutableData dataWithData:GIF_buffer];
unsigned char aBuffer[9];
[GIF_buffer getBytes:aBuffer length:9];
CGRect rect;
rect.origin.x = ((int)aBuffer[1] << 8 ) | aBuffer[0];
rect.origin.y = ((int)aBuffer[3] << 8 ) | aBuffer[2];
rect.size.width = ((int)aBuffer[5] << 8 ) | aBuffer[4];
rect.size.height = ((int)aBuffer[7] << 8 ) | aBuffer[6];
AnimatedGifFrame *frame = [GIF_frames lastObject];
frame.area = rect;
if (aBuffer[8] & 0×80) GIF_colorF = 1; else GIF_colorF = 0;
unsigned char GIF_code = GIF_colorC, GIF_sort = GIF_sorted;
if (GIF_colorF == 1)
{
GIF_code = (aBuffer[8] & 0×07);
if (aBuffer[8] & 0×20)
{
GIF_sort = 1;
}
else
{
GIF_sort = 0;
}
}
int GIF_size = (2 << GIF_code);
size_t blength = [GIF_screen length];
unsigned char bBuffer[blength];
[GIF_screen getBytes:bBuffer length:blength];
bBuffer[4] = (bBuffer[4] & 0×70);
bBuffer[4] = (bBuffer[4] | 0×80);
bBuffer[4] = (bBuffer[4] | GIF_code);
if (GIF_sort)
{
bBuffer[4] |= 0×08;
}
NSMutableData *GIF_string = [NSMutableData dataWithData:[[NSString stringWithString:@"GIF89a"] dataUsingEncoding: NSUTF8StringEncoding]];
[GIF_screen setData:[NSData dataWithBytes:bBuffer length:blength]];
[GIF_string appendData: GIF_screen];
if (GIF_colorF == 1)
{
[self GIFGetBytes:(3 * GIF_size)];
[GIF_string appendData:GIF_buffer];
}
else
{
[GIF_string appendData:GIF_global];
}
// Add Graphic Control Extension Frame (for transparancy)
[GIF_string appendData:frame.header];
char endC = 0x2c;
[GIF_string appendBytes:&endC length:sizeof(endC)];
size_t clength = [GIF_screenTmp length];
unsigned char cBuffer[clength];
[GIF_screenTmp getBytes:cBuffer length:clength];
cBuffer[8] &= 0×40;
[GIF_screenTmp setData:[NSData dataWithBytes:cBuffer length:clength]];
[GIF_string appendData: GIF_screenTmp];
[self GIFGetBytes:1];
[GIF_string appendData: GIF_buffer];
while (true)
{
[self GIFGetBytes:1];
[GIF_string appendData: GIF_buffer];
unsigned char dBuffer[1];
[GIF_buffer getBytes:dBuffer length:1];
long u = (long) dBuffer[0];
if (u != 0×00)
{
[self GIFGetBytes:u];
[GIF_string appendData: GIF_buffer];
}
else
{
break;
}
}
endC = 0x3b;
[GIF_string appendBytes:&endC length:sizeof(endC)];
// save the frame into the array of frames
frame.data = GIF_string;
}
- (bool) GIFGetBytes: (int) length
{
if (GIF_buffer != nil)
{
[GIF_buffer release]; // Release old buffer
GIF_buffer = nil;
}
if ([GIF_pointer length] >= dataPointer + length) // Don’t read across the edge of the file..
{
GIF_buffer = (id)[[GIF_pointer subdataWithRange:NSMakeRange(dataPointer, length)] retain];
dataPointer += length;
return YES;
}
else
{
return NO;
}
}
- (bool) GIFSkipBytes: (int) length
{
if ([GIF_pointer length] >= dataPointer + length)
{
dataPointer += length;
return YES;
}
else
{
return NO;
}
}
- (NSArray *)frames
{
NSMutableArray *frames = [NSMutableArray arrayWithCapacity:100];
for(int i=0; i<100; i++)
{
UIImage *aImage = [self getFrameAsImageAtIndex:i];
if(!aImage)
break;
else
[frames addObject:aImage];
}
return frames;
}
@end
初始化方法
LoadingAnimationView继承自UIImageView
@implementation LoadingAnimationView
@synthesize gifs = _gifs;
- (id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
AnimatedGif *aniGif = [[AnimatedGif alloc] init];
NSString *path = [[NSBundle mainBundle] pathForResource:@”loading” ofType:@”gif”];
[aniGif decodeGIF:[NSData dataWithContentsOfFile:path]];
_gifs = [[aniGif frames] retain];
self.animationImages = _gifs;
self.animationDuration = 0.1f*[_gifs count];
self.animationRepeatCount = 9999;
[aniGif release];
}
return self;
}
-(void)startAnimating
{
if(self.hidden)
self.hidden = NO;
[super startAnimating];
}
-(void)stopAnimating
{
[super stopAnimating];
self.hidden = YES;
}
- (void)dealloc {
[_gifs release];
[super dealloc];
}