I'm trying to use the UIImagePickerController interface from OS 3.1, with the cameraOverlayView and takePicture, but I've clearly failed to understand how this works, and so I'm not getting the behaviour I want.
What I want to do is open the camera and take a picture automatically without having to having the user interact with the picker or edit the image. So I subclass UIImagePickerController (similar to the example in http://github.com/pmark/Helpful-iPhone-Utilities/tree/master/BTL%20Utilities/) and turn off all of the controls:
- (void)displayModalWithController:(UIViewController*)controller animated:(BOOL)animated {
self.sourceType = UIImagePickerControllerSourceTypeCamera;
self.showsCameraControls = NO;
self.navigationBarHidden = YES;
self.toolbarHidden = YES;
// Setting the overlay view up programmatically.
ipView = [[ImagePickerView alloc] init];
self.cameraOverlayView = ipView;
[controller presentModalViewController:self animated:NO];
}
In the overlayView, I've managed to force the takePicture method of UIImagePickerController to fire (I know this, because I can NSLog it, and I hear the sound of the camera taking a picture). The overlayView shows up just fine. However, the delegate method didFinishPickingMediaWithInfo: never gets called, and imagePickerControllerDidCancel doesn't get called either.
So, how do I either get the delegate methods to get called, or save the picture by overriding the takePicture method? (I have no idea how to capture the picture data here, and Google seems to have failed me). I can't help feeling that I've failed to understand how the guts of UIImagePickerController works, but the docs aren't overly helpful:
e.g.:
"You can provide a custom overlay view to display a custom picture-taking interface and you can initiate the taking of pictures from your code. Your custom overlay view can be displayed in addition to, or instead of, the default controls provided by the image picker interface."
or from showCameraControls:
"If you set this property to NO and provide your own custom controls, you can take multiple pictures before dismissing the image picker interface." - How do I dismiss the picker interface?
Note: the delegate is set properly in IB, so that's not the problem.
Thanks for any help you can provide!
I've found that you just have to wait "long enough" before calling takePicture, or it just silently fails. I don't have a good answer for how to determine the minimum value of "long enough" that will always work, but if you set a timer and wait five or ten seconds you should be okay. It would be nice if it returned some kind of an "I'm not ready to take a picture yet, sorry" error either directly from takePicture or through the delegate, but as far as I know it doesn't.
As an update to my own question: It turns out that I was trying to use takePicture too early. When I moved the action to a button on the overlay and sent takePicture from that button (once the picker was presented modally), the delegate methods fired as they should. I don't know if what I wanted is achievable - taking the image without having to press that button, automatically - but if it is, it will probably have to be done by sending takePicture sometime after I was trying to use it.
-(void)imageMethod:(id)sender{
imagePickerController = [[UIImagePickerController alloc]init];
imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
imagePopover=[[UIPopoverController alloc]initWithContentViewController:imagePickerController];
[imagePopover presentPopoverFromRect:importButton.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
}
Related
I have an app where i am using UIImagePickerController to use the native camer inorder to click pictures but the when the photo gallery on the device is full. I get a alert message which says "Cannot Take Photo - There is not enough available storage to take a photo.You can manage your storage in Settings". I am given two options to click the "Done" button or "Settings" button. Clicking either of them does nothing and the app freezes completely.
This is what i get from the console logs
Not enough space to take a picture. Available space is 0
The code for the picker
UIImagePickerController *mediaPicker = [[UIImagePickerController alloc] init];
mediaPicker.sourceType = UIImagePickerControllerSourceTypeCamera;
mediaPicker.delegate=self;
mediaPicker.sourceType=UIImagePickerControllerSourceTypeCamera;
[self presentModalViewController:mediaPicker animated:YES];
I have implemented and tried all the delegates already and its not calling any delegate.
Is there any way i can implement something where i can use a listener to detect when this error occurs and take back the user to the previous screen ?
Sounds like your device run out of memory, system sent lots of "Out of Memory" notifications and your app got one, too. As result your app released the UIViewController, which originally launched UIImagePickerController.
Now when you dismiss imagePicker with Done/Settings button, control returns back to your app. The old UIViewController doesn't exist any more and you haven't implemented code to recreate it from scratch in this kind of situations. The device looks like it frozen, but only because UI wasn't redrawn by your app. Otherwise app works just fine.
You can check this case by implementing didReceiveMemoryWarning method into every UIViewController and logging, if it's called:
- (void)didReceiveMemoryWarning
{
NSLog(#"%#", [self description]);
[super didReceiveMemoryWarning];
}
One of my favourite bugs. Easy to miss :)
This sounds like a bug in iOS.
You should file a feedback at https://feedbackassistant.apple.com/.
Is it a known issue that if you try to test your UIImagePickerController using the Camera as a source type then the simulator will crash?
I have the following code:
self.imgPicker = [[UIImagePickerController alloc] init];
self.imgPicker.allowsEditing = NO;
self.imgPicker.delegate = self;
[self.imgPicker setSourceType:UIImagePickerControllerSourceTypeCamera];
[self presentModalViewController:self.imgPicker animated:YES];
[imgPicker release];
Running this in simulator, I get an objc-exception-throw error on the stack # -[UIImagePickerController setSourceType:].
Now if I set the source type to the Photo Library though, everything runs smoothly and fine? What's the deal?
Simulator doesn't have the camera and can't simulate to take a picture (it would have been nice to use the isight but Apple has not been so kindly). However Your code is not safe because, for example, old ipod touch doesn't have a camera and in this case your app will crash on this device.
As Apple suggest in UIImagePickerController documentation:
To use an image picker controller
containing its default controls,
perform these steps:
1.Verify that the device is capable of picking content from the
desired source. Do this calling the
isSourceTypeAvailable: class method,
providing a constant from the
“UIImagePickerControllerSourceType”
enum.
2.Check which media types are available, for the source type you’re
using, by calling the
availableMediaTypesForSourceType:
class method. This lets you
distinguish between a camera that can
be used for video recording and one
that can be used only for still
images.
3.Tell the image picker controller to adjust the UI according to the
media types you want to make
available—still images, movies, or
both—by setting the mediaTypes
property.
4.Present the user interface by calling the
presentModalViewController:animated:
method of the currently active view
controller, passing your configured
image picker controller as the new
view controller.
5.When the user taps a button to pick a newly-captured or saved image
or movie, or cancels the operation,
dismiss the image picker using your
delegate object. For newly-captured
media, your delegate can then save it
to the Camera Roll on the device. For
previously-saved media, your delegate
can then use the image data according
to the purpose of your app.
So you have to call isSourceTypeAvailable and set your sourceType consistently.
I'm using a custom overlay view and showsCameraControls = NO. When I'm done I dismissModalViewControllerAnimated:YES. What appears to happen is that the iris appears fully closed (ie. no closing animation - just poof and it's closed) and then immediately slides down off the screen.
As a test I manually called viewWillDisappear on the UIImagePickerController and that makes the closed iris appear, but again no smooth animation.
I also tried wrapping the dismiss in a long animation transaction and that just made the re-appearence of the underlying navigation toolbar slow down. The iris behaved just as above.
I don't want to have to make my own iris animation - that would be uncool!
PS: Using sdk 4.0
To partially answer my own question, the best I have been able to come up with so far is:
- (void)imagePickerController:(UIImagePickerController*)picker
didFinishPickingMediaWithInfo:(NSDictionary*)info
{
[picker viewWillDisappear:YES];
[self performSelector:#selector(processPickerImage:)
withObject:[[info objectForKey:UIImagePickerControllerOriginalImage] retain]
afterDelay:0.1];
}
-(void) processPickerImage:(UIImage *)uiImage
{
// do stuff
[self dismissModalViewControllerAnimated:YES];
// dismiss your custom overlay etc.
[uiImage release];
}
It doesn't actually animate the iris, but at least it is onscreen immediately so the user recognises that the photo taking is done. I'm also not super happy that viewWillDisappear gets called twice on the UIImagePickerController - I'm not sure it's guaranteed to be safe.
Also the status bar appears over the iris which is annoying.
I'm hoping someone else has a better solution?
I have designed an iris shutter animation for a camera view in an iPhone app.
Unfortunately, it seems impossible to hide Apple's shutter when the view appears, even if I hide the camera controls and create a custom cameraOverlayView.
I have gotten around this by animating my shutter on top of the normal shutter when the view appears, using the viewWillAppear and viewDidAppear methods of UIImagePickerController. However, I can't get the shutter to be hidden under my shutter the first time through. When the app launches, it shows a camera view, and the original shutter is visible. On all subsequent views of the cameraController, my workaround works. Any suggestions?
Here's my code. This is from my app delegate:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
cameraController = [[CameraController alloc] initWithMode:#"camera"];
[window addSubview:cameraController.view];
}
And this is from my UIImagePickerController subclass:
- (void) viewWillAppear:(BOOL)animated {
if (self.sourceType != UIImagePickerControllerSourceTypePhotoLibrary || simulatorView) {
[self addShutter];
[shutter close];
}
[super viewWillAppear:animated];
}
- (void) viewDidAppear:(BOOL)animated {
if (self.sourceType != UIImagePickerControllerSourceTypePhotoLibrary || simulatorView) {
[shutter openShutter:.5f];
}
[super viewDidAppear:animated];
}
Note that the docs say that subclassing UIImagePickerController isn't supported, so it may work in some cases but isn't "safe". Not sure if it would get rejected by the app store. (Probably depends on how picky their static code verification tool is.)
I don't really have a good answer, but you might try either 1) iterating over the subviews of the picker's main view to see if you can identify whatever is being used to animate the shutter, then mangle it so that it won't display, or 2) for the initial animation, just show the initial image picker main view under another opaque black view. Not sure if the user-specified overlay view would work for that or not, but you might be able to do those without subclassing.
Searching for undocumented subviews is another thing that's theoretically unsafe though since who knows how the implementation might change in the future.
Possibly too late, but my proposal is to use the following notifications (found while debugging)
PLCameraControllerAvailable - camera controller object is initiated, but shutter is not visible yet
PLCameraViewIrisAnimationDidEndNotification - iris animation is completed.
And the usage is straightforward: call UIGetScreenImage() on 1st notification, render grabbed image on screen (fullscreen) just above the UIImagePicker. Destroy rendered image on the 2nd notification.
I try the same thing with no results, so I do this workaround:
1- Suppose you have a method called showAllButtons with no parameters that will show all your custom things (buttons, tool bars,...)
2- Initialize all the custom controls hidden
3- Write a method that will call the last function but within an interval:
-(void)showAllButtonsDelayed:(NSTimeInterval)a_iMsToDelay
{
NSTimer* tmpShowButtonsTimer = [NSTimer timerWithTimeInterval:a_iMsToDelay target:self selector:#selector(showAllButtons) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:tmpShowButtonsTimer forMode:NSDefaultRunLoopMode];
}
4- Call that method in the willDidAppear method of the UIImagePickerController subclass. Play with some values of a_iMsToDelay.
Hope this helps.
I am creating a landscape only application using sdk 3.0 that uses mapkit. I need to use iphone camera in my application. But I am getting following warning when I try to open camera.
"Can't perform full-screen transition. The fromViewController's view must be within a view that occupies the full screen."
The view from which I am calling camera method is mapview with size of 480*320. I have written following code to call camera:
UIImagePickerController *picker = [[UIImagePickerController alloc]init];
picker.delegate = self;
picker.sourceType=UIImagePickerControllerSourceTypeCamera;
[self presentModalViewController: picker animated:YES];
[picker release];
After that I have written the usual method :
-(void)imagePickerController : (UIImagePickerController *)picker didFinishPickingImage : (UIImage *)image editingInfo : (NSDictionary *)editingInfo
But this is never called , since camera is never opened. So my question is what am I missing here...? I am testing this app on actual iphone device , not on simulator. I have used this code in another app and it works fine. But here, it simply doesnt work! Plz help if you have any idea about this..
it sounds like the warning is telling you the problem: the parent view you're passing in to presentModalViewController needs to be a full-screen view. So instead of using "self" in this code you need to use something else, like the parent view controller.
now, you're going to have another problem, because the camera controller doesn't like landscape mode, so you may have to switch back to portrait mode before showing it....
just replace "self" with the parent ViewController which is probably declared in you appDelegate.