I set up a UIImagePickerController modal view to record a video, and once the user chooses "Use Video" it dismisses the view controller fine and does exactly what I'd like. However, as soon as I add a cameraOverlayView, which is just a UIView with nothing special going on, didFinishPickingMediaWithInfo is never called. Even if I put an NSLog on the first line of that function, I don't see any output. More oddly still, UIImagePickerControllerDidCancel still gets called when the user presses the Cancel button.
Prior suggestions on SO do not seem helpful as I have set the delegate and set the editing properties accordingly unlike in these popular posts:
uiimagepickercontroller didfinishpickingmediawithinfo NOT CALLED when selecting a video from the library
UIImagePickerController didFinishPickingMediaWithInfo not being called
Here's the code to launch the UIImagePickerController (taken from a Ray Wenderlich tutorial):
-(BOOL)startCameraControllerFromViewController:(UIViewController*)controller
usingDelegate:(id )delegate {
if (([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] == NO)
|| (delegate == nil)
|| (controller == nil)) {
return NO;
}
if(![delegate conformsToProtocol:#protocol(UIImagePickerControllerDelegate) ]) {
NSAssert(nil, #"delegate muste conforms to UIImagePickerControllerDelegate protocol");
}else{
NSLog(#"delegate does conform to protocol");
}
CGRect screenRect = [[UIScreen mainScreen]bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat screenHeight = screenRect.size.height;
CameraOverlay *overlay = [[CameraOverlay alloc]initWithFrame:CGRectMake(0, 0, screenWidth, screenHeight)];
UIImagePickerController *cameraUI = [[UIImagePickerController alloc] init];
cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
cameraUI.delegate = delegate;
cameraUI.mediaTypes = [[NSArray alloc] initWithObjects:(NSString *)kUTTypeMovie, nil];
cameraUI.allowsEditing = NO;
//if I comment out the line below, everything works fine.
//but if I don't comment this out, didFinishPickingMediaWithInfo is never called
cameraUI.cameraOverlayView = overlay;
[controller presentModalViewController: cameraUI animated: YES];
return YES;
}
Here's the part of didFinishPickingMediaWithInfo:
(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSLog(#"did finish picking media with info");
NSString *mediaType = [info objectForKey: UIImagePickerControllerMediaType];
[self dismissModalViewControllerAnimated:NO];
// I also tried [picker dismissModalViewControllerAnimated:NO];
//despite it not being its own delegate, but in any case
//this did not change the result
...
}
I added the valid delegate check following Andrey's advice, but now I always see "delegate does conform to protocol" regardless of whether the overlay is in and didFinishPickingMediaWithInfo is called.
Any suggestions for what to try or errors in the code? Thanks for taking a look.
Here's what finally worked: set userInteractionEnabled to FALSE for the overlay view.
I wish I knew whether this was a bug or a feature. I looked at a few tutorials for creating overlay views, and none mentioned setting this property. If anyone can fully explain why this made the difference, I'd like to mark a more complete answer than this.
#sunny finally correct solution! userInteractionEnabled = FALSE did call didFinishPickingMediaWithInfo
Related
I'm meeting another strange problem....
I allow users to take photo. To do that, I'm using UIImagePickerController.
With iPhone, it's working, but with iPad (iOS 8, I don't know for others versions), I have a black screen preview.
This is my code :
- (BOOL) startCameraControllerFromViewController: (UIViewController*) controller usingDelegate: (id <UIImagePickerControllerDelegate, UINavigationControllerDelegate>) delegate {
if (([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera] == NO) || (delegate == nil) || (controller == nil))
return NO;
UIImagePickerController *cameraUI = [[UIImagePickerController alloc] init];
cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
cameraUI.allowsEditing = NO;
cameraUI.delegate = delegate;
[self presentViewController:cameraUI animated:YES completion:NULL];
return YES;
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSLog(#"photo taken");
}
So when the user click on the button, I've the view to take the photo which appears. But the screen is black, and when I click on the button to take the photo, there is nothing.... and didFinishPickingMediaWithInfo isn't called.
(I see more topics, but I don't found any solution for this problem.)
Thanks,
Make sure you run UIKit operations on main thread. Check also if you app has permissions to use camera [in device settings].
This can also be an issue.(If it helps someone.)
Try this its solved my problem, make sure that there is a value
(Application name as string) in your info.plist > "Bundle display name".
In my case it was empty and because of that it didn't work.
If "Bundle display name" is not there in the info.plist,then add a row named "Bundle display name" and paste your appname .
Hi Im making altered reality app. My main controller is derived from UIImagePickerController. Here is how I create it:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!self.overlayController) {
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
self.sourceType = UIImagePickerControllerSourceTypeCamera;
self.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
self.cameraDevice = UIImagePickerControllerCameraDeviceRear;
self.showsCameraControls = false;
self.allowsEditing = false;
self.overlayController = [[[ControlsViewController alloc] initWithNibName:#"ControlsViewController" bundle:[NSBundle mainBundle] picker:self] autorelease];
[self.view addSubview:self.overlayController.view]; // need to add as subview otherwise mouse events captured by UIImagePickerController
self.overlayController.view.frame = CGRectMake(0,0, self.view.frame.size.width, self.view.frame.size.height);
}
}
[cameraControls setCamera:self];
}
Basically controller exists through whole lifecycle of the app. Now when I take photo with code below most of the time it works fine but sometime didFinishPickingMediaWithInfo isn't called. Usual behavior is I see camera focus starts adjust blur-in/out and then it stabilizes but didFinishPickingMediaWithInfo will never be called. Looks like if camera goes into some kind of calibration mode it may not fire this event. Anyone have solution? I hope there is maybe some kind of extra callback (like error processing Im missing).
I can even hear camera shutters simulated sound interrupted when camera goes into that weird calibration mode.
//self.cameraPicker points to the instance of my main controller that I created earlier
-(void) setCamera:(UIImagePickerController *)picker {
self.cameraPicker = picker;
picker.delegate = self;
}
-(void) takePicture {
[self.cameraPicker takePicture];
}
-(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSLog(#"Picture ready");
}
I have some code which works on iPhone 5 but not on iPhone 5S or iPad Air. I know that the iPhone 5 is 32 bit and the other devices are 64 bit, but I can't see how this would cause my problem. I use a camera overlay with a button on a tabbar to capture my picture.
I can instantiate the UIImagePickerController like so:
UIImagePickerController *pick = [[UIImagePickerController alloc] init];
pick.modalPresentationStyle = UIModalPresentationCurrentContext;
pick.sourceType = UIImagePickerControllerSourceTypeCamera;
pick.delegate = self;
pick.wantsFullScreenLayout=YES;
/*
The user wants to use the camera interface. Set up our custom overlay view for the camera.
*/
CGSize screenBounds = [UIScreen mainScreen].bounds.size;
CGFloat cameraAspectRatio = 4.0f/3.0f;
CGFloat camViewHeight = screenBounds.width * cameraAspectRatio;
CGFloat scale = screenBounds.height / camViewHeight;
pick.cameraViewTransform = CGAffineTransformMakeTranslation(0, (screenBounds.height - camViewHeight) / 2.0);
pick.cameraViewTransform = CGAffineTransformScale(pick.cameraViewTransform, scale, scale);
pick.showsCameraControls = NO;
/*
Load the overlay view from the OverlayView nib file. Self is the File's Owner for the nib file, so the overlayView outlet is set to the main view in the nib. Pass that view to the image picker controller to use as its overlay view, and set self's reference to the view to nil.
*/
[[NSBundle mainBundle] loadNibNamed:IS_IPAD ? #"OverlayView-ipad" : #"OverlayView" owner:self options:nil];
self.overlayView.frame = pick.cameraOverlayView.frame;
self.focusRect.layer.borderColor = [UIColor colorWithRed:255 green:0 blue:0 alpha:1.0].CGColor;
pick.cameraOverlayView = self.overlayView;
self.overlayView = nil;
self.imagePickerController = pick;
[self presentViewController:self.imagePickerController animated:YES completion:nil];
The focusRect is a Red rectangle on the view to show users how to center their photo.
I have added the protocols to my .h file: UIImagePickerControllerDelegate, UINavigationControllerDelegate
I call takePicture with an IBAction. The following works on 32 bit, but not 64:
- (IBAction)takePicture:(id)sender{
[self.imagePickerController takePicture];
[self dismissViewControllerAnimated:YES completion:NULL];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *chosenImage = info[UIImagePickerControllerOriginalImage]; //64 bit never gets here
//Do some stuff.
}
The symptoms are just that neither didFinishPickingMediaWithInfo nor imagePickerControllerDidCancel ever get executed on 64 bit devices. My overlay is dismissed, but the code in these methods only executes on 32 bit devices.
I have checked several other questions and answers, for example this. Several suggestions from different threads were to make sure that the delegate is being set to self, adding the proper protocols to the view, and making sure the method name is typed correctly. I believe that these are covered.
I have confirmed that the app is running as 32 bit. Due to a dependency, I have the Valid Architectures set to "arm7 arm7s" without arm64. I am using XCode5.I would appreciate any help you can offer. Thank you for looking.
Looks like this was actually not related to the bitness of the device, but how they handled ARC differently.
I solved the problem by moving the dismissViewControllerAnimated from takePicture into the didFinishPickingMediaWithInfo routine like so:
- (IBAction)takePicture:(id)sender{
[self.imagePickerController takePicture];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *chosenImage = info[UIImagePickerControllerOriginalImage]; //64 bit never gets here
//Do some stuff.
[self dismissViewControllerAnimated:YES completion:NULL];
}
My theory is that the object was being destroyed before the delegate method could be called.
When i try to load camera from my code, camera preview is black. If I wait for 10-20 seconds it will show real camera preview. I found several questions and some of them suggest that running some other code in background should be the reason for this. However I don't have any code running in background.
How should I fix this?
This is my code where I run camera
UIImagePickerController *photoPicker = [[UIImagePickerController alloc] init];
photoPicker.delegate = self;
photoPicker.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentViewController:photoPicker animated:YES completion:NULL];
About 5 months ago my team discovered a memory leak with UIImageViewController in iOS7. Each instantiation slowed down the app exponentially (i.e. first alloc-init had a 1 second delay, second had a 2 second delay, third had a 5 second delay). Eventually, we were having 30-60 delays (similar to what you're experiencing).
We resolved the issue by subclassing UIImagePickerController and making it a Singleton. That way it was only ever initialized once. Now our delay is minimal and we avoid the leak. If subclassing isn't an option, try a class property in your viewController and just lazy load it like so.
-(UIImagePickerController *)imagePicker{
if(!_imagePicker){
_imagePicker = [[UIImagePickerController alloc]init];
_imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
}
return _imagePicker;
}
Then you can just call it later like:
[self presentViewController:self.imagePicker animated:YES completion:nil];
Had this myself - it happens if something is running on the main dispatch thread - are you resizing images by any chance?
It puts the preview onto the main thread and if something is using it, you get a black screen. It's a bug and the workaround is to either take over the main thread or to disable the photo picker until the queue is free
This Should work for you:
- (void)cameraViewPickerController:(UIImagePickerController *)picker
{
[self startCameraControllerFromViewController: picker
usingDelegate: self];
}
- (BOOL) startCameraControllerFromViewController: (UIViewController*) controller
usingDelegate: (id <UIImagePickerControllerDelegate,
UINavigationControllerDelegate>) delegate {
if (([UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypeCamera] == NO)
|| (delegate == nil)
|| (controller == nil))
return NO;
UIImagePickerController *cameraUI = [[UIImagePickerController alloc] init];
cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
// Displays a control that allows the user to choose movie capture
cameraUI.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *) kUTTypeImage, (NSString *) kUTTypeMovie,nil];
// Hides the controls for moving & scaling pictures, or for
// trimming movies. To instead show the controls, use YES.
cameraUI.allowsEditing = NO;
cameraUI.delegate = delegate;
[controller presentViewController:cameraUI animated:YES completion:nil];
return YES;
}
I use standart image picker to make some camera photo.
When user makes photo image picker shows him the Preview screen with 2 buttons "Retake" and "Use".
How to detect that Preview screen is active now or "Retake" button pressed? Is it possible ? Are the useful properties or events? Something like when image source is library the is property - allows editing, which shows similar screen .
UIImagePickerController * imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
A bit after the fact, but maybe someone is still seeking this answer like I was. If you want continue using the native camera controls, you can check the subviews of the ImagePickerController to determine if the post-record view is showing.
BOOL videoTaken = NO;
for (UIView *aView in self.imagePickerController.view.subviews[0].subviews[0].subviews[0].subviews)
{
if ([aView isKindOfClass:NSClassFromString(#"PLTileContainerView")])
{
videoTaken = YES;
break;
}
}
The "PLTileContainerView" is the subview that contains the editing slider that lets you view your video frame by frame, so if it's present, that means your video has already recorded.
For use:
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[picker dismissModalViewControllerAnimated:NO];
NSString *type = [info objectForKey:#"UIImagePickerControllerMediaType"];
if ([type isEqualToString:#"public.movie"]) {
} else {
UIImage *image = [info objectForKey:#"UIImagePickerControllerOriginalImage"];
}
}
For Cancel you don't have a way of detecting it (other than subclassing UIImagePickerController, which may be prohibited, or other way that I'm not aware), but for sure the second cancel is detectable :
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[picker dismissModalViewControllerAnimated:YES];
}