Fork me on GitHub

10/02/2010

[iPhone] 了解 PhotoPicker 當中的委派機制

最近想要實作一個 iPhone 手機上的照相軟體,因此稍微研究了一下有關 camera 相關的資料。首先 iPhone 當中處理 camera 的主要是 UIImagePickerController 這個 class。利用 UIImagePickerControllerDelegate 和 UIImagePickerController 就可以開始處理/控制 iPhone 的 camera 和 photo library。

在 Beginning iPhone3 Development 書中,關於 Camera 的章節範例,主要是以 UIViewerController 下的 ModalView ( - (void)presentModalViewController:(UIViewController *)modalViewController animated:(BOOL)animated ) 模式來呈現擷取到的照片,這個模式下的照片並非全螢幕,仍保留一些 Nav Bar 或者 Tool Bar,跟一般看到的 app 可以在拍照完以後在全螢幕模式下編輯不太一樣。

在 Apple Developer 網站當中有一個 PhotoPicker 的範例,主要就是在演示,如何不使用 ModalView 的方式取得 camera 所拍攝到的原始影像,在將這個影像填入你要呈現的 View 當中。

該範例當中宣告了兩個 ViewController:MyViewController 和 OverlayViewController,前者主要就是客製化的 View 想要用來避開 ModalView 模式的方式。後者的 .h 如下程式碼宣告,這個 class 當中包含了一個 UIImageViewController 並且還有一個 遵守 OverlayViewControllerDelegate 協定的成員 "delegate"。

@protocol OverlayViewControllerDelegate;

@interface OverlayViewController : UIViewController <UINavigationControllerDelegate, 
UIImagePickerControllerDelegate>
{
    id <OverlayViewControllerDelegate> delegate;
    
    UIImagePickerController *imagePickerController;
    
@private
    ...
}    

@property (nonatomic, assign) id <OverlayViewControllerDelegate> delegate;
@property (nonatomic, retain) UIImagePickerController *imagePickerController;

...

@end

@protocol OverlayViewControllerDelegate
- (void)didTakePicture:(UIImage *)picture;
- (void)didFinishWithCamera;
@end


可以看到這個 OverlayViewControllerDelegate 當中列出了兩個必要實現的方法 didTakePicture 和 didFinishWithCamera。這兩個方法的名稱看起來像是系統發出的事件,但是先前提到,有關照相相關的任務主要是以 UIImagePickerController 這個 class 所控制,所以來看看 OverlayViewControllerDelegate 的 .m 檔。

#pragma mark -
#pragma mark UIImagePickerControllerDelegate

// this get called when an image has been chosen from the library or taken from the camera
//
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    UIImage *image = [info valueForKey:UIImagePickerControllerOriginalImage];
    
    // give the taken picture to our delegate
    if (self.delegate)
        [self.delegate didTakePicture:image];
    
    if (![self.cameraTimer isValid])
        [self finishAndUpdate];
}

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    [self.delegate didFinishWithCamera];    // tell our delegate we are finished with the picker
}

@end

可以看到在實現 UIImagePickerControllerDellegate 的兩個方法當中,偷偷地把事件和影像的資訊 委派給 OverlayViewControllerDelegate protocol 的方法,那麼誰負責做這個 protocol 當中描述的這兩件事呢,答案就是 MyViewController!

- (void)viewDidLoad
{
    self.overlayViewController =
        [[[OverlayViewController alloc] initWithNibName:@"OverlayViewController" bundle:nil] autorelease];

    // as a delegate we will be notified when pictures are taken and when to dismiss the image picker
    self.overlayViewController.delegate = self;
    
    self.capturedImages = [NSMutableArray array];

    if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
    {
        // camera is not on this device, don't show the camera button
        NSMutableArray *toolbarItems = [NSMutableArray arrayWithCapacity:self.myToolbar.items.count];
        [toolbarItems addObjectsFromArray:self.myToolbar.items];
        [toolbarItems removeObjectAtIndex:2];
        [self.myToolbar setItems:toolbarItems animated:NO];
    }
}


// as a delegate we are being told a picture was taken
- (void)didTakePicture:(UIImage *)picture
{
    [self.capturedImages addObject:picture];
}

// as a delegate we are told to finished with the camera
- (void)didFinishWithCamera
{
    [self dismissModalViewControllerAnimated:YES];
    
    if ([self.capturedImages count] > 0)
    {
        if ([self.capturedImages count] == 1)
        {
            // we took a single shot
            [self.imageView setImage:[self.capturedImages objectAtIndex:0]];
        }
        else
        {
            // we took multiple shots, use the list of images for animation
            self.imageView.animationImages = self.capturedImages;
            
            if (self.capturedImages.count > 0)
                // we are done with the image list until next time
                [self.capturedImages removeAllObjects];  
            
            self.imageView.animationDuration = 5.0;    // show each captured photo for 5 seconds
            self.imageView.animationRepeatCount = 0;   // animate forever (show all photos)
            self.imageView.startAnimating;
        }
    }
}


上面 MyViewController 實現碼當中可以看到,他包含了一個 OverlayViewController 物件,並且他將該物件的代理設定為 "自己" ( self.overlayViewController.delegate = self; ),最後他實現了這個 OverlayViewControllerDelegate 這 protocol 的兩個方法,將 UIImagePickerController 得到的影像資料神奇的存到自己的成員 capturedImages 當中。

簡而言之,OverlayViewController 將 UIImagePickerControllerDelegate 和 UIImagePickerController 包裝起來,所以富有預設的圖片顯示功能,但是他並沒有用 Modal 的方式,而是將他取得的照片,傳給自己的 delegate 成員。而 MyViewController 有一個 OverlayController 成員,又將自己指向 OverlayController 的 delegate 成員。因此等效於 UIImagePickerController 把取得的照片傳給 MyViewController。

......

No comments:

Post a Comment