开发者

Image storage in iOS devices, the appropriate behavior?

I'm kinda puzzeled about image storage in iOS devices for an app i'm making.

My requirement is to load an Image onto a tableViewCell, lets say in the default Image space of a UITableViewCell and hence isnt a background image.

Now The user can either add an Image either via the PhotoDirectory or take an entirely new image.

If a new image is taken, where should that image be stored preferebly? In the default photo directory ? or in the documents folder of the app sandbox?

Because these are image file开发者_如何学Gos, I'm afraid that store images within the app bundle can make it pretty big, I'm afraid I dont wana cross the size limit.

Performance wise though... what would be a better option?


I have an app that also does some of the things you describe. My solutions was to create a singleton that I call my imageStore. You can find information about a singleton here

In this imageStore, I store all my "full size" images; however, like you I am concerned about the size of these images, so instead of using them directly, I use thumbnails. What I do is this. For each object that I want to represent in the table, I make sure the object has a UIImage defined that is about thumnail size (64x64 or any size you desire). Then an object is created, I create a thumbnail that I store along with the object. I use this thumbnail instead of the larger images where I can get a way with it, like on a table cell.

I'm not behind my Mac at the moment, but if you want I can post some code later to demonstrate both the singleton and the creation and usage of the thumbnail.

Here is my header file for the ImageStore

#import <Foundation/Foundation.h>

@interface BPImageStore : NSObject {
    NSMutableDictionary *dictionary;
}

+ (BPImageStore *)defaultImageStore;

- (void)setImage:(UIImage *)i forKey:(NSString *)s;
- (UIImage *)imageForKey:(NSString *)s;
- (void)deleteImageForKey:(NSString *)s;

@end

Here is the ImageStore.m file - my Singleton

#import "BPImageStore.h"

static BPImageStore *defaultImageStore = nil;

@implementation BPImageStore

+ (id)allocWithZone:(NSZone *)zone {
    return [[self defaultImageStore] retain];
}

+ (BPImageStore *)defaultImageStore {
    if(!defaultImageStore) {
        defaultImageStore = [[super allocWithZone:NULL] init];
    }
    return defaultImageStore;
}

- (id)init
{

    if(defaultImageStore) {
        return defaultImageStore;
    }

    self = [super init];
    if (self) {
        dictionary = [[NSMutableDictionary alloc] init];
        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
        [nc addObserver:self selector:@selector(clearCach:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
    }

    return self;
}

- (void) clearCache:(NSNotification *)note {
    [dictionary removeAllObjects];
}

- (oneway void) release {
    // no op
}

- (id)retain {
    return self;
}

- (NSUInteger)retainCount {
    return NSUIntegerMax;
}

- (void)setImage:(UIImage *)i forKey:(NSString *)s {
    [dictionary setObject:i forKey:s];

    // Create full path for image
    NSString *imagePath = pathInDocumentDirectory(s);

    // Turn image into JPEG data
    NSData *d = UIImageJPEGRepresentation(i, 0.5);

    // Write it to full path
    [d writeToFile:imagePath atomically:YES];

}

- (UIImage *)imageForKey:(NSString *)s {
    // if possible, get it from the dictionary
    UIImage *result = [dictionary objectForKey:s];
    if(!result) {
        // Create UIImage object from file
        result = [UIImage imageWithContentsOfFile:pathInDocumentDirectory(s)];
        if (result)
            [dictionary setObject:result forKey:s];
    }
    return result;
}

- (void)deleteImageForKey:(NSString *)s {
    if(!s) {
        return;
    }
    [dictionary removeObjectForKey:s];
    NSString *path = pathInDocumentDirectory(s);
    [[NSFileManager defaultManager] removeItemAtPath:path error:NULL];
}

@end

Here is where I use the image store. In my Object "player", I have a UIImage to store the thumbnail and I have an NSString to house a key that I create. Each original image I put into the store has a key. I store the key with my Player. If I ever need the original image, I get by the unique key. It is also worth noting here that I don't even store the original image at full size, I cut it down a bit already. After all in my case, it is a picture of a player and nobody has too look so good as to have a full resolution picture :)

 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    NSString *oldKey = [player imageKey];
    // did the player already have an image?
    if(oldKey) {
        // delete the old image
        [[BPImageStore defaultImageStore] deleteImageForKey:oldKey];
    }


    UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];

    // Create a CFUUID object  it knows how to create unique identifier
    CFUUIDRef newUniqueID = CFUUIDCreate(kCFAllocatorDefault);

    // Create a string from unique identifier
    CFStringRef newUniqueIDString = CFUUIDCreateString(kCFAllocatorDefault, newUniqueID);

    // Use that unique ID to set our player imageKey
    [player setImageKey:(NSString *)newUniqueIDString];

    // we used Create in the functions to make objects, we need to release them
    CFRelease(newUniqueIDString);
    CFRelease(newUniqueID);

    //Scale the images down a bit
    UIImage *smallImage = [self scaleImage:image toSize:CGSizeMake(160.0,240.0)];


    // Store image in the imageStore with this key
    [[BPImageStore defaultImageStore] setImage:smallImage
                                        forKey:[player imageKey]];

    // Put that image onto the screen in our image view
    [playerView setImage:smallImage];

    [player setThumbnailDataFromImage:smallImage];
}

Here is an example of going back to get the original image from the imageStore:

// Go get image
NSString *imageKey = [player imageKey];
if (imageKey) {
    // Get image for image key from image store
    UIImage *imageToDisplay = [[BPImageStore defaultImageStore] imageForKey:imageKey];
    [playerView setImage:imageToDisplay];
} else {
    [playerView setImage:nil];
}

Finally, here is how I create a thumbnail from the original image:

- (void)setThumbnailDataFromImage:(UIImage *)image {
CGSize origImageSize = [image size];

CGRect newRect;
newRect.origin = CGPointZero;
newRect.size = [[self class] thumbnailSize]; // just give a size you want here instead

// How do we scale the image
float ratio = MAX(newRect.size.width/origImageSize.width, newRect.size.height/origImageSize.height);

// Create a bitmap image context
UIGraphicsBeginImageContext(newRect.size);

// Round the corners
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:newRect cornerRadius:5.0];

[path addClip];

// Into what rectangle shall I composite the image
CGRect projectRect;
projectRect.size.width = ratio * origImageSize.width;
projectRect.size.height = ratio *origImageSize.height;
projectRect.origin.x = (newRect.size.width - projectRect.size.width) / 2.0;
projectRect.origin.y = (newRect.size.height - projectRect.size.height) / 2.0;

// Draw the image on it
[image drawInRect:projectRect];

// Get the image from the image context, retain it as our thumbnail
UIImage *small = UIGraphicsGetImageFromCurrentImageContext();
[self setThumbnail:small];

// Get the image as a PNG data
NSData *data = UIImagePNGRepresentation(small);
[self setThumbnailData:data];

// Cleanup image context resources
UIGraphicsEndImageContext();

}

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜