15

I've noticed that in OS 3.1 you can add an overlay view to the image picker with

cameraOverlayView

However, I've also noticed that adding a view via this method also displays the view for the entire time the UIImagePicker is displayed, when I only want to show it during the preview stage.

Is there a way to make this only happen during preview? I don't want it to be there during the open shutter animation, or the screen that asks whether you want to use the image or retake.

mac_55
  • 4,130
  • 7
  • 31
  • 40

4 Answers4

19

I just wanted to show up another possibility without subview fiddling. There are system notifications for the changes in the CameraPicker. They are not documented either (as the subview structure). So they are also subject to change. BUT you get them.

You can register for them using no name:

[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(notificationReceived:) name: nil object: nil];

We get some interesting (selfexplaining) notifications here:

PLCameraControllerPreviewStartedNotification
PLCameraViewIrisAnimationWillBeginNotification
PLCameraViewIrisAnimationDidEndNotification

Recorder_DidStartPreviewing
Recorder_SourceStarted
Recorder_WillCapturePhoto
Recorder_DidCapturePhoto
Recorder_PhotoStillImageSampleBufferReady
Recorder_DidStopPreviewing

_UIImagePickerControllerUserDidCaptureItem // Photo was taken, preview is shown
_UIImagePickerControllerUserDidRejectItem  // Repeat was pressed, camera is shown again 

You could use them, to trigger your overlayviews hidden state or anything else.

calimarkus
  • 9,955
  • 2
  • 28
  • 48
  • You think you could get your app rejected for using these? – Javier Soto Feb 20 '12 at 14:59
  • No, these are just strings. This should work. But I haven't tested it yet. – calimarkus Mar 01 '12 at 15:14
  • 1
    Tested this, and I was able to successfully change up my preview to be different from my camera view by latching on to "_UIImagePickerControllerUserDidCaptureItem" notification. I did notice that neither PLCameraViewIrisAnimationWillBeginNotification nor PLCameraViewIrisAnimationDidEndNotification were fired (I'm using iOS 6), so I had to work around by using notifications around those events, such as "Recorder_WillCapturePhoto" and "PLCameraControllerPreviewStartedNotification". – Christian Sep 23 '12 at 07:06
  • does this still work? I can not seem to get it to work. – joshLor Oct 24 '18 at 00:19
18

I've figured out a way for you to achieve the desired result though it's a bit... not so standard. :)

The idea is to rearrange a bit the order of the inner views in the structure UIImagePickerController uses.

OK, so we create an UIImagePickerController object, initialize it and add an overlay view to it. May I have your attention please! The UIView object (UIImageView in the example code) is hidden from the very beginning. Don't miss that. Finally we present the image picker controller as modal view controller. This code should be somewhere in your applicationDidFinishLaunching:, viewWillAppear: or similar appropriate about to launch methods.

UIImagePickerController *anImagePickerController = [UIImagePickerController new];
anImagePickerController.delegate = self;
anImagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;

UIImageView *anImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Watermark.png"]];
anImageView.frame = CGRectMake(0, 1, anImageView.image.size.width, anImageView.image.size.height);
anImageView.hidden = YES;
anImagePickerController.cameraOverlayView = anImageView;

[viewController presentModalViewController:anImagePickerController animated:NO];
[anImagePickerController release];

[NSTimer scheduledTimerWithTimeInterval:0.1
                                 target:self
                               selector:@selector(timerFireMethod:)
                               userInfo:anImageView
                                repeats:YES];
[anImageView release];

Before the overlay view (anImageView) is released a NSTimer is created, initialized with anImageView (NSTimer userInfo property) and scheduled right away. Here's the method it calls:

- (void)timerFireMethod:(NSTimer*)theTimer {
    UIView *cameraOverlayView = (UIView *)theTimer.userInfo;
    UIView *previewView = cameraOverlayView.superview.superview;

    if (previewView != nil) {
        [cameraOverlayView removeFromSuperview];
        [previewView insertSubview:cameraOverlayView atIndex:1];

        cameraOverlayView.hidden = NO;

        [theTimer invalidate];
    }
}

The whole NSTimer thing is added to the flow to ensure that the reordering work around will happen exactly when the UIImagePickerController will be totally ready for that.

This is it. It works, it's not standard, it's rough and quick. Again feel free to optimize and make it 'righter' (oh please do, my aim was to show you the way).

Ivan Karpan
  • 1,554
  • 15
  • 20
  • I was thinking of setting the view to display after a timer has run, however I wasn't sure if the camera always takes the same length of time to load - doesn't it depend on how much memory is in use at the time, and what device it's running on? e.g. it'll load faster on a 3GS than on a 3G. – mac_55 Jan 23 '10 at 15:39
  • Hey mac_55, I've optimized my solution - now it works perfecto (as perfecto as a work around solution can work). :) HTH – Ivan Karpan Jan 23 '10 at 20:13
  • Sorry for the delay. I haven't got around to testing this yet, but I'm happy to mark it as the answer. :) – mac_55 Jan 25 '10 at 00:15
  • really, you simply use the NotificationCenter ... it's a line of code. http://stackoverflow.com/questions/17942444/uiimagepicker-cameraoverlayview-appears-on-retake-screen – Fattie Feb 19 '14 at 15:52
6

Actually, I was trying to do the same thing and I came across a much simpler of doing it via this blog post: http://www.alexcurylo.com/blog/2009/06/18/uiimagepickercontroller-in-3-0/

Since the UIImagePickerController can call a UINavigatorControllerDelegate, the method below gets called before the Image picker is displayed.

Here's the important code:

 - (void)navigationController:(UINavigationController *)navigationController
willShowViewController:(UIViewController *)viewController
animated:(BOOL)animated
{
   if (!viewController)
   return;

   UIView* controllerViewHolder = viewController.view;
   UIView* controllerCameraView = [[controllerViewHolder subviews] objectAtIndex:0];
   UIView* controllerPreview = [[controllerCameraView subviews] objectAtIndex:0];
   [controllerCameraView insertSubview:self.overlayView aboveSubview:controllerPreview];
}

Hope that helps.

naragon
  • 61
  • 1
-2

When you build up the picker add the overlay to the picker (picker.cameraOverlayView = myView).

The overlay will not be in the picture taken. It is just, like it says, an overlay view.

You can remove the overlay view before taking a picture (takePicture method) and add the view again in imagePickerController:didFinishPickingMediaWithInfo:

diederikh
  • 25,221
  • 5
  • 36
  • 49
  • Sorry, this is not what I'm looking for. I want to display the overlay only before taking the picture. Not whilst the camera shutter is animating, or whilst it is asking you whether you like the picture you took – mac_55 Jan 17 '10 at 18:57
  • If you remove the overlay (picker.cameraOverlayView = nil) before calling takePicture then the overlay will not be there when the shutter is animating. – diederikh Jan 17 '10 at 19:28
  • But how do I show it on the preview screen? Am I right in assuming that takePhoto captures whatever is in the preview screen? But I want to display the overlay on the preview screen (before the photo is taken) *confused* – mac_55 Jan 17 '10 at 19:53
  • I think you are misunderstanding what I'm trying to do. I realise that the overlay is not included in the picture taken, but it *is* on the screen throughout the capture process - I don't want it to be shown on the screen for all this time, only on the screen where the user sees the 'take picture' button. – mac_55 Jan 18 '10 at 14:26