2012-06-06 72 views
4

我使用以下代碼來允許我的應用程序的用戶拍攝/選擇照片,然後將其保存到文檔目錄並設置爲一個UIImageView的形象:保存使用相機拍攝的圖像或從相機膠捲中選擇的長時間延遲 - iPhone

-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { 

     if (actionSheet.tag == 0){ 
      if (buttonIndex == 0) { 
       NSLog(@"Take Picture Button Clicked"); 
       // Create image picker controller 
       UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init]; 

       // Set source to the camera 
       imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; 

       // Delegate is self 
       imagePicker.delegate = self; 

       // Show image picker 
       [self presentModalViewController:imagePicker animated:YES]; 
      } 
      else if (buttonIndex == 1) { 
       NSLog(@"Choose From Library Button Clicked"); 
       // Create image picker controller 
       UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init]; 

       // Set source to the camera 
       imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; 

       // Delegate is self 
       imagePicker.delegate = self; 

       // Show image picker 
       [self presentModalViewController:imagePicker animated:YES]; 
      } 
      else if (buttonIndex == 2) { 
       NSLog(@"Cancel Button Clicked"); 
      } 
     } 
...... 

- (void)saveImage:(UIImage*)image:(NSString*)imageName 
{ 
    NSData *imageData = UIImagePNGRepresentation(image); //convert image into .png format. 

    NSFileManager *fileManager = [NSFileManager defaultManager];//create instance of NSFileManager 

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //create an array and store result of our search for the documents directory in it 

    NSString *documentsDirectory = [paths objectAtIndex:0]; //create NSString object, that holds our exact path to the documents directory 

    NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", imageName]]; //add our image to the path 

    [fileManager createFileAtPath:fullPath contents:imageData attributes:nil]; //finally save the path (image) 

    receiptImageView1.image = [UIImage imageWithContentsOfFile:fullPath]; 
    self.receiptImage1 = fullPath; 

    NSLog(@"image saved"); 
} 

//Receive the image the user picks from the image picker controller 
-(void)imagePickerController:(UIImagePickerController*)picker 
didFinishPickingMediaWithInfo:(NSDictionary*)info { 
    UIImage* image = [info objectForKey: UIImagePickerControllerOriginalImage]; 
    NSString* imageName = @"Receipt1Image1"; 
    [self saveImage:image :imageName]; 
} 

基本上我的問題是,這種代碼似乎非常緩慢執行,例如,當我選擇從相機膠捲的圖像它最終保存,給我回調用視圖,但只有經過很長的延遲..

任何人都可以在這方面的任何指示?

+0

使用Time Profiler工具可以找出哪些部件是最慢的,並嘗試並優化這些部件。 –

回答

8

保存較大的圖像(如iPhone 4/4S中相機拍攝的圖像)需要很長時間。如果你分析這個過程,你會發現UIImagePNGRepresentation()需要一段時間才能生成你的PNG圖像,但我的經驗中的主要瓶頸是寫入1 MB以上圖像的磁盤。

除了使用JPEG壓縮,我發現在我的基準測試中速度更快,或者使用更快的第三方圖像壓縮例程,您幾乎無法加速此過程。因此,如果您不想在發生這種情況時阻止用戶界面,請將此保存進程分派到後臺線程或隊列中。你可以做類似如下:

-(void)imagePickerController:(UIImagePickerController*)picker 
didFinishPickingMediaWithInfo:(NSDictionary*)info { 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
     UIImage* image = [info objectForKey: UIImagePickerControllerOriginalImage]; 
     NSString* imageName = @"Receipt1Image1"; 
     [self saveImage:image :imageName]; 
    }); 
} 

然而,注意一個事實,即這些UIImages中使用了相當多的內存,以保持周圍,所以你可能需要使用一個調度信號,以防止更多比一次這樣的圖像保存操作不會同時發生。

此外,作爲一個文體音符,定義等

- (void)saveImage:(UIImage*)image:(NSString*)imageName 

一個Objective-C方法,同時允許的,是高度氣餒。爲每個參數指定一個名稱,如下所示:

- (void)saveImage:(UIImage*)image fileName:(NSString*)imageName 

這會使您的代碼更具描述性。

+0

這不適用於ios7版本的iPhone 5c,但適用於iOS 4版本的iPhone 4s。爲什麼這樣? –

+0

像魅力一樣工作。謝謝@Brad Larson –

2

我已經回答了similar question。爲了清晰起見,我將在這裏複製它:

根據圖像分辨率的不同,UIImagePNGRepresentation確實可能非常慢,正如任何寫入文件系統一樣。

您應該總是在異步隊列中執行這些類型的操作。即使性能看起來對您的應用程序來說足夠好,但在測試時,您仍然應該使用 - 您永遠不會知道該設備可能會執行哪些其他進程,這可能會在您的應用掌握之後減慢存儲速度的用戶。

使用Grand Central Dispatch(GCD),較新版本的iOS可以非常容易地進行異步存儲。的步驟是:

  1. 創建NSBlockOperation節省了圖像
  2. 在塊操作的完成塊,從磁盤讀取&顯示它的圖象。唯一需要注意的是,您必須使用主隊列來顯示圖像:所有UI操作必須發生在主線程上。
  3. 將塊操作添加到操作隊列並觀察它!

就是這樣。這裏的代碼:

// Create a block operation with our saves 
NSBlockOperation* saveOp = [NSBlockOperation blockOperationWithBlock: ^{ 

    [UIImagePNGRepresentation(image) writeToFile:file atomically:YES]; 
    [UIImagePNGRepresentation(thumbImage) writeToFile:thumbfile atomically:YES]; 

}]; 

// Use the completion block to update our UI from the main queue 
[saveOp setCompletionBlock:^{ 

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 

     UIImage *image = [UIImage imageWithContentsOfFile:thumbfile]; 
     // TODO: Assign image to imageview 

    }]; 
}]; 

// Kick off the operation, sit back, and relax. Go answer some stackoverflow 
// questions or something. 
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
[queue addOperation:saveOp]; 

一旦你對這種代碼模式感到滿意,你會發現自己使用它很多。這在生成大型數據集,加載長時間操作等方面非常有用。從本質上來說,任何使您的用戶界面不受影響的操作都是該代碼的理想選擇。請記住,當你不在主隊列時,你不能對UI做任何事情,而其他所有事情都是蛋糕。

相關問題