10

On my app I have a cameraOverlayView over my open camera with custom controls for the camera buttons. The app allows the user to take several pictures before closing the camera, so the shutter button does not call dismissViewControllerAnimated, instead there's a close button for when you're done taking pictures.

Now, one of the buttons on the camera overlay is a gallery button to allow the user to pick a saved image instead of shooting a new one. I've tried two different approaches to make this work, both failed.

First approach

Use the same UIImagePickerController instance that is currently presenting the overlay and switch the sourceType to library. It does present the gallery then, but when a photo is tapped, I can't dismiss the galley without dismissing the whole overlay.

Second approach

Create a separate instance of UIImagePickerController, set the sourceType to gallery and attempt to call presentViewController, which then fails with the warning:

"Warning: Attempt to present on whose view is not in the window hierarchy!"

Does anyone have a solution for this issue? Is this even possible?

Vineet Singh
  • 4,009
  • 1
  • 28
  • 39
Rafael
  • 368
  • 1
  • 4
  • 22
  • 1
    Yes it's possible. Can you show us your code for your second approach? I think you may be calling it on a wrong place. Have a look at this: http://stackoverflow.com/a/12320222/361247 – Enrico Susatyo Jan 23 '13 at 00:25
  • I tried the second approach.It goes fine.I think you may check for your source.Maybe it is the problem as Enrico Susatyo commented.Good luck~~ – Steven Jiang Jan 28 '13 at 06:46

5 Answers5

14

Try this~~ I think it is your goal.

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, strong) UIImagePickerController *imagePicker;

@end

@implementation ViewController

@synthesize imagePicker = _imagePicker;

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

}


- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:YES];

    sleep(2);

    _imagePicker = [[UIImagePickerController alloc] init];
    [_imagePicker setSourceType:UIImagePickerControllerSourceTypeCamera];
    [_imagePicker setDelegate:self];

    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 50, 100, 30)];
    [button setTitle:@"Library" forState:UIControlStateNormal];
    [button setBackgroundColor:[UIColor darkGrayColor]];
    [button addTarget:self action:@selector(gotoLibrary:) forControlEvents:UIControlEventTouchUpInside];

    [_imagePicker.view addSubview:button];

    [self presentViewController:_imagePicker animated:YES completion:nil];
}
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(IBAction)gotoLibrary:(id)sender
{
    UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
    [imagePicker.view setFrame:CGRectMake(0, 80, 320, 350)];
    [imagePicker setSourceType:UIImagePickerControllerSourceTypeSavedPhotosAlbum];
    [imagePicker setDelegate:self];

    [_imagePicker presentViewController:imagePicker animated:YES completion:nil];
}


- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    [picker dismissViewControllerAnimated:YES completion:nil];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    [picker dismissViewControllerAnimated:YES completion:nil];
}
@end
Steven Jiang
  • 1,006
  • 9
  • 21
  • Did you test this? It seems it's what I am doing on my second approach and it's throwing the error. – Rafael Jan 29 '13 at 16:31
  • Of course.Click the button in camera view, it shows the photo library view.I just made a new project, edited the .m file, and connected view in the storyboard to the controller's view.That's all I did. – Steven Jiang Jan 30 '13 at 01:55
  • Why the sleep call in viewDidAppear. I took mine out and it still worked. What are we waiting for? If the delay is really needed? Ts there a more deterministic way implement this? – SilentNot Dec 12 '13 at 00:16
  • What does this line do? [imagePicker.view setFrame:CGRectMake(0, 80, 320, 350)]; – jjxtra Mar 23 '14 at 18:41
  • 3
    Please don't ever call sleep(2); on the main thread >< like that. Use [self performSelector:afterDelay] instead. – Isaac Paul Jun 12 '14 at 16:01
  • ^ What he said. You can use performSelector after delay or dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(time, dispatch_get_main_queue(), ^(void){} Never lock the thread especially in the view life cycle calls – TheCodingArt Jun 12 '14 at 16:08
3

The second approach is correct. I don't see your code, but I think that your controller hierarchy is something similar to this:

Main VC ---present---> Camera VC

so, if you call

[self presentViewController:picker animated:YES completion:^{}];

from your Main VC, you are attempting to show another VC from an "hidden" one (covered by the Camera VC).

The key is to take a reference to your camera VC (let's call it cameraVC) and do something similar from Main VC:

 [cameraVC presentViewController:theOtherPicker animated:YES completion:^{}];

doing this, the "present" action is done by the Camera VC (visible) without warnings, and not by the hidden Main VC.

LombaX
  • 17,265
  • 5
  • 52
  • 77
1

You could code your own custom gallery views for selecting photos and then add that to the cameraOverlayView subview hierarchy.

There must be open source projects on GitHub, which show how to make these views, somewhere. Alternatively, I happen to have released a control very similar to what you are looking for if you don't want to start from scratch.

It's quite a simple procedure really — a collection view with a datasource backed by the AssetsLibrary framework.

Benjamin Mayo
  • 6,649
  • 2
  • 26
  • 25
1

I would set up a capture session as follows:

- (void)setupCaptureSession
{
    NSError* error = nil;

    // Create the session
    _captureSession = [[AVCaptureSession alloc] init];    
    _captureSession.sessionPreset = AVCaptureSessionPresetMedium;
    AVCaptureDevice* device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    AVCaptureDeviceInput* input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];

    [_captureSession addInput:input];
    AVCaptureVideoDataOutput* output = [[AVCaptureVideoDataOutput alloc] init];
    [_captureSession addOutput:output];

    // Configure your output.
   dispatch_queue_t queue = dispatch_queue_create("myCameraOutputQueue", NULL);
   //If you want to sebsequently use the data, then implement the delegate.
   [output setSampleBufferDelegate:self queue:queue]; 
}

Having done that, you can create a preview layer as follows:

_previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_captureSession];
[_captureSession startRunning];

And then add the preview layer to your view:

_myView.layer addSubLayer:_previewLayer];

Now that you have this set up, I'd add a custom image picker, such as this one: https://github.com/arturgrigor/AGImagePickerController

Jasper Blues
  • 28,258
  • 22
  • 102
  • 185
  • This custom ImagePicker presents itself as modal, and I already have an open camera modal, I don't think this would work. – Rafael Jan 29 '13 at 12:23
  • It doesn't have to be modal, I'm sure you could work around that with all the source. . by the way: Its one of many on gitbub. I chose that one because it included pictures. . . . having said that, at first glance, it seems like there are a couple of other good answers on here. . . – Jasper Blues Jan 29 '13 at 12:25
  • This error I mentioned on my questions happens if I try to present the gallery ImagePicker over the camera ImagePicker: `code`[self presentViewController:galleryImagePicker animated:YES completion:nil]; – Rafael Jan 29 '13 at 12:31
1

I present a custom camera view controller, onto which I add a "SourceTypeCamera" UIImagePickerController as a contained child view controller. My custom view controller has a button that in turn adds another UIImagePickerController instance, this one a "SourceTypePhotoLibrary".

You end up with a nested view controller hierarchy:

  • root/main view controller — presents:
  • my custom camera view controller – adds as child view controller:
  • either camera or photo-library UIImagePickerController

Something like this:

- (void)viewDidLoad { (id<UIImagePickerControllerDelegate,UINavigationControllerDelegate>)theDelegate {
    [super viewDidLoad];
    [self startImagePickerWithSourceType:UIImagePickerControllerSourceTypeCamera
                                delegate:self];
    // configure button in camera overlay to call -pickPhotoTapped
}

- (void) pickPhotoTapped {
    [self startImagePickerWithSourceType:UIImagePickerControllerSourceTypePhotoLibrary
                                delegate:self];
}

- (BOOL) startImagePickerWithSourceType:(UIImagePickerControllerSourceType)sourceType
                               delegate:(id<UIImagePickerControllerDelegate,UINavigationControllerDelegate>)theDelegate {
    if (([UIImagePickerController isSourceTypeAvailable:sourceType] == NO)
            || (theDelegate == nil))
        return NO;

    self.cameraUI = [[UIImagePickerController alloc] init];
    self.cameraUI.sourceType = sourceType;
    self.cameraUI.view.frame = self.view.bounds;
    self.cameraUI.delegate = theDelegate;

    if (sourceType == UIImagePickerControllerSourceTypeCamera) {
        self.cameraUI.allowsEditing = NO;
        self.cameraUI.showsCameraControls = NO;
        self.cameraUI.cameraOverlayView = [self overlayView];
    }

    [self addChildViewController:self.cameraUI];
    [self.view addSubview:self.cameraUI.view];
    [self.cameraUI didMoveToParentViewController:self];

    return YES;
}
Yang Meyer
  • 5,409
  • 5
  • 39
  • 51
  • My problem is that I want to present the gallery OVER the camera, so BOTH, not EITHER, UIImagePickerControllers must be presented. – Rafael Jan 29 '13 at 12:27
  • Try using this approach, but instantiate a second UIImagePickerController instance and add it to the existing UIImagePickerController instance as a child view controller. If you want a transition like sliding up from the bottom, animate frame change or use +[UIView transitionFromView:toView:duration:options:completion:] – Yang Meyer Jan 29 '13 at 13:06
  • @Rafael What it is your device??I showed both of the views in iPad when trying your second approach by popover the library view. – Steven Jiang Jan 29 '13 at 14:42
  • @StevenJiang it's an iPhone-only app. – Rafael Jan 29 '13 at 16:27
  • @Yang Will this work? I can't test right now unfortunately. Should I add the second UIImagePickerController using [UIView addSubView] ? It seems this method won't work as it states in the Class Reference: "The starting view for the transition. By default, this view is removed from its superview as part of the transition." – Rafael Jan 29 '13 at 16:30