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");
}
Related
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
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;
}
Iphone app, IOS 5 and up.
There are similar questions to this one on SO but I haven't encountered this exact scenario so I'll ask. I'm editing an existing app that allows lets you take a photo which it resizes and sends to a web service.
I need to add the ability to take 3 photos, resize each and send to the same service. I thought it would be just a matter of repeating what was already in the app but it uses the UIImagePickerController which apparently only allows one photo per use.
So the way it works is that there's a 'Take Photo' button which calls the method below. Once that photo is taken another button appears that says 'Take another photo' (I added this button) and I have it calling the same method but it is just copying over the previous photo, which is to be expected really. How should I best alter this to accommodate 3 photos?
This is the takephoto method that I'm calling.
- (IBAction)takePhoto:(id)sender
{
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
imagePickerController.delegate = self;
[self presentViewController:imagePickerController animated:YES completion:NULL];
}
check this https://developer.apple.com/library/ios/samplecode/photopicker/Introduction/Intro.html
, check the take photo part of this demo project.
Eventually figured out how to do this. I added a tag to the button calling the method, through IB.
Then in the takephoto method I assigned the imagePickerController a tag based on the tag of the button that tapped. Like this:
- (IBAction)takePhoto:(id)sender
{
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
if([sender tag] == 2)
{
imagePickerController.view.tag = 2;
}
//And so on...
imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
imagePickerController.delegate = self;
[self presentViewController:imagePickerController animated:YES completion:NULL];
}
Then in the ImagePickerController didFinishedPickingMediaWithInfo method:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
if(picker.view.tag == 2)
{
//Do stuff here
}
}
So, I'll probably have to create three different buttons for each of the three possible photos, I'm sure there's a better way than that but it should work.
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];
}