将图片放入SQLite数据存储区进行压缩的最快方法是什么,所以我可以将控制权交还给用户?
UIImagePickerController
在我的应用程序中拍照。由于UIImageJPEGRepresentation
。我想要达到的目标是速度足够快,让用户感觉很快。
我该如何处理?还有什么我应该知道的吗?
答案 0 :(得分:4)
基于评论和测试,这是我目前正在做的事情:
当我从UIImageController
获取图像时,我将其保留在类ivar中并关闭图像选择器。我显示一个阻止我的主视图的视图,并安排一个NSTimer事件在一秒钟内进行压缩,然后返回给调用者。
这样就可以运行动画来解散图像控制器。我的拦截视图显示在它下面。
(拦截器视图填充导航控制器的整个内容区域,并且是纯黑色,带有UIActivityIndicatorView
。)
- (void)imagePickerController: (UIImagePickerController *)picker
didFinishPickingImage: (UIImage *)selectedImage
editingInfo: (NSDictionary *)editingInfo;
{
busyView.userInteractionEnabled = YES;
busyView.alpha = 0.7f;
mainView.userInteractionEnabled = NO;
[self dismissModalViewControllerAnimated: YES];
[NSTimer scheduledTimerWithTimeInterval: 1.0f
target: self
selector: @selector(compress:)
userInfo: selectedImage
repeats: NO];
}
当计时器触发时,我使用JPEG压缩图像(因为它比PNG更快,尽管直觉)并且逐渐消失阻挡视图。
- (void)compress: (NSTimer *)inTimer;
{
[self gotJPEG: UIImageJPEGRepresentation( inTimer.userInfo, 0.5f )];
[UIView beginAnimations: @"PostCompressFade" context: nil];
[UIView setAnimationDuration: 0.5];
busyView.userInteractionEnabled = NO;
busyView.alpha = 0.0f;
[UIView commitAnimations];
mainView.userInteractionEnabled = YES;
}
虽然这会增加一秒钟的处理时间,但它会让图像选择器更快地移开,因此不再感觉我的应用程序已经冻结。 UIActivityIndicatorView
的动画确实在UIImageJPEGRepresentation
正在运行时运行。
比使用NSTimer
延迟1秒的更好的答案是在dismissModalViewControllerAnimated:
的动画结束时获得一个事件,但我不知道该怎么做。
(我认为这还没有解决。)
答案 1 :(得分:2)
您不应该在数据库中保存图片,除非它的尺寸非常小。确定图片是否足够小的阈值当然是非常主观的。根据我的拙见(以及iPhone上的体验),它不应超过1兆字节。因此,您应该只在数据库中保存小尺寸图像,如图标,缩略图等。对于超过1兆字节的图像,您只需将它们作为文件存储在文件系统中,并将文件名(图像路径)放在数据库中。顺便说一下,将图像存储在文件系统及其路径名中的速度非常快。
关于压缩:你当然可以使用另一个线程压缩图像,但要考虑它是否真的值得这样做。您可以使用线程将图像保存到文件中,将路径名保存在数据库中并立即将控件返回给用户。即使在最新的iPhone 3GS上,您(通常)也有足够的空间,但计算能力非常小。此外,您应该验证(我真的不知道这一点)是否通过UIImageView加载压缩图像需要更多时间w.r.t.非压缩的,如PNG。如果您的应用程序在加载压缩图像时会产生额外的开销,那么绝对不值得压缩您的图像。它基本上是空间和速度之间的权衡。希望这有助于决定。
答案 2 :(得分:0)
使用ios 5的父级子管理对象上下文:
我的托管对象上下文按此顺序排列:
persistent store coordinator --->
Private Queue Managed Object Context ( for saving to disk in background) ----->
Main Queue Managed Object Context (for UI) ----->
Misc. Private Managed Object Contexts (for temporary jobs like UIImagePNGRepresentation() for example)
模型如下:
Image Entity -> title : string , image : relationship(ImageBlob) optional
ImageBlob Entity -> image : Binary Data, imageEntity : relationship(Image)
关系反转已设定。
用户完成图片选择后:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
// get the main queue managed object context
NSManagedObjectContext* mainQueueManagedObjectContext = self.managedObjectContext;
// get the image
UIImage* image = [info objectForKey:UIImagePickerControllerOriginalImage];
// create an object, using the managed object context for the main queue
NSManagedObject *newImage = [NSEntityDescription insertNewObjectForEntityForName:@"Image" inManagedObjectContext:mainQueueManagedObjectContext];
// edit not expensive properties
[newImage setValue:[NSString stringWithFormat:@"new title %i", [self tableView:self.tableView numberOfRowsInSection:0]] forKey:@"title"];
// lets save the main context to get a permanant objectID
[self saveContextForManagedObjectContext:mainQueueManagedObjectContext];
// get the permenant objectID, Thread Safe..
NSManagedObjectID* imageObjectID = newImage.objectID;
// create a private queue concurrent managed object context
NSManagedObjectContext* privateQueueManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
// set the main queue as the parent
[privateQueueManagedObjectContext setParentContext:mainQueueManagedObjectContext];
// we have to use blocks here, as this managed object context will work in a private queue
[privateQueueManagedObjectContext performBlock:
^{
// get the png representation in background
NSData* data = UIImagePNGRepresentation(image);
// get the managed object using the thread safe objectID
NSManagedObject* imageObjectInPrivateQueue = [privateQueueManagedObjectContext objectWithID:imageObjectID];
// insert a new object for the ImageBlob entity
NSManagedObject *imageBlobInPrivateQueue = [NSEntityDescription insertNewObjectForEntityForName:@"ImageBlob" inManagedObjectContext:privateQueueManagedObjectContext];
// set our image data
[imageBlobInPrivateQueue setValue:data forKey:@"image"];
// set the relationship to the original record
[imageObjectInPrivateQueue setValue:imageBlobInPrivateQueue forKey:@"image"];
// save changes to private queue context to main queue context
[self saveContextForManagedObjectContext:privateQueueManagedObjectContext];
// since we are not in the main queue, we have to ask the main managed object context using performBlock
[mainQueueManagedObjectContext performBlock:
^{
// what time is it before launching save in main queue
NSDate* startDate = [NSDate date];
// launch save on main queue
[self saveContextForManagedObjectContext:mainQueueManagedObjectContext];
// what time is it after finishing save in main queue
NSDate* finishDate = [NSDate date];
// see how long UI blocked
NSLog(@"blocked UI for %f seconds", [finishDate timeIntervalSinceDate:startDate]);
}];
}];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
[self.popOverController dismissPopoverAnimated:YES];
}
else
{
[self dismissViewControllerAnimated:YES completion:nil];
}
}
这就是保存的方式:
-(void)saveContextForManagedObjectContext:(NSManagedObjectContext*)managedObjectContext
{
// Save the context.
NSError *error = nil;
if (![managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
这大大减少了用户界面上的阻塞,在iphone 4上,选择一个500万像素的图像只会阻止用户界面0.015秒。
另一方面,加载图像也会阻止UI显着的时间,所以你也可以在后台加载它。