I am using an UIImagePickerController within an UIPopoverController which is working perfectly with iOS6. With iOS 7 the "preview" image which is shown to capture the image is rotated, but if I take a picture it is saved correctly.
This is how I get my picker:
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
imagePicker.mediaTypes = [NSArray arrayWithObjects:
(NSString *) kUTTypeImage,
nil];
imagePicker.allowsEditing = NO;
And add it to a popover controller:
self.imagePickerPopOver = [[UIPopoverController alloc] initWithContentViewController:imagePicker];
[self.imagePickerPopOver presentPopoverFromRect:CGRectMake(aPosViewA.x, cameraButton_y, 100.0, 30.0) inView:self.detailViewController.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
Those are calculations for the button position at a UIScrollView to show the popover at the correct position:
presentPopoverFromRect:CGRectMake(aPosViewA.x, cameraButton_y, 100.0, 30.0)
I don't think that the problem lies there as I have tried out several combinations.
I have also tried to capture the image in fullscreen-mode, but the app is only allowed to use landscape mode. If the image is taken in portrait-mode and the modal view is dismissed, the app stays in portrait mode as well. I couldn't find a way to prevent the UIImagePickerController to switch to portrait mode or to force the app back to landscape mode if the modal view was dismissed.
UPDATE
I have taken the answer from here and came a step further.
I transform the view after creating the picker and before showing the popover :
switch ([UIApplication sharedApplication].statusBarOrientation) {
case UIInterfaceOrientationLandscapeLeft:
self.imagePicker.view.transform = CGAffineTransformMakeRotation(M_PI/2);
break;
case UIInterfaceOrientationLandscapeRight:
self.imagePicker.view.transform = CGAffineTransformMakeRotation(-M_PI/2);
break;
default:
break;
}
which works as long as i don't turn around the iPad. For that I am registering for the orientation changed event:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
and change the picker view:
- (void)orientationChanged:(NSNotification *)notification{
if (self.imagePicker) {
switch ([UIApplication sharedApplication].statusBarOrientation) {
case UIInterfaceOrientationLandscapeLeft:
self.imagePicker.view.transform = CGAffineTransformMakeRotation(M_PI/2);
break;
case UIInterfaceOrientationLandscapeRight:
self.imagePicker.view.transform = CGAffineTransformMakeRotation(-M_PI/2);
break;
default:
break;
}
}
}
REMAINING PROBLEM: As I wrote in the beginning, when the picture was taken, it was shown correctly to accept or dismiss it. This is now transformed as well. Somehow I need to know when the image is taken and transform it back.
AND, this is really a nasty hack and probably won't work with the next iOS Update. Has anybody an idea how to implement that in a cleaner way?
UPDATE 2
This was too nasty, I have found a cleaner solution which solves my problem but is not the answer to the initial question regarding an imagepicker in a popover controller, which is not recommended by Apple, but allowed.
I have subclassed now the UIImagePickerController like this:
@implementation QPImagePickerController
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return UIInterfaceOrientationIsLandscape(toInterfaceOrientation);
}
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskLandscape;
}
@end
and I am using the imagepicker in fullscreen instead in a popover. Tested so far in iOS7.
I had a similar situation in my app. However the preview was rotating correctly in iOS7 and not in iOS8. This code assumes you have more than one orientation.
The first thing is to subclass
UIImagePickerController
.Starting from the top, add
#import <AVFoundation/AVFoundation.h>
to your .m file.Also add a property to save the initial orientation
@property (nonatomic) UIInterfaceOrientation startingOrientation;
and another for a condition to remove clipping@property (nonatomic) BOOL didAttemptToRemoveCropping;
.Were going to listen to a couple of notifications.
UIApplicationDidChangeStatusBarOrientationNotification
is obviously to listen for the device rotation.AVCaptureSessionDidStartRunningNotification
is called right when the camera starts capturing.In the
-captureSessionDidStart:
add a condition to verify the view is actually on screen and to make sure the camera is supposed to be displayedif (self.view.window && self.sourceType == UIImagePickerControllerSourceTypeCamera)
. If so, set the starting orientationself.startingOrientation = [UIApplication sharedApplication].statusBarOrientation;
.In the
-statusBarOrientationDidChange:
add the same condition as above, but this time if true, we'll update the camera transform. First we get the offset rotation based on the initial rotation. This is needed when you enter theUIImagePickerController
in orientations other then portrait.Next we'll update the camera transform with the current rotation.
And finally we'll attempt to remove the black bars presented in either 90 degree orientation from the starting orientation. (This might only be an iOS8 issue.) In slightly more detail, if I enter the
UIImagePickerController
in portrait mode and then rotate to landscape, there will be black bars on the top and bottom of the preview. The solution for this is not to scale but rather to remove the clipping of a superview. We only need to make this attempt once, so first check if we have called this code. Also make sure that we only call this code if we have rotated. If its called in the initial orientation it wont work right away.Finally in the code for
-findClippedSubviewInView:
we loop through all the subviews to search for a view with.clipsToBounds = YES
. If thats true we make one more condition to verify one of its ancestral superviews is correct.In the
-hasAncestorCameraView:
we simply loop up the superview chain and return true if one of the classes hasCameraView
in the name.Thats the breakdown of code, here's it all together.
iPad with Camera - don't display in a popover. Instead, present in a modal view controller, like you would on the iPhone. (at least starting with iOS 7)
I've found another solution which handles the case where a device is rotated while the UIIMagePickerView is presenting based on the answer Journeyman provided. It also handles the case where the view is rotated from UIOrientationLandscapeRight/UIOrientationLandscapeLeft back to UIOrientationPortrait.
I created a subclass of UIImagePickerController:
Then registered it to receive notifications if the device orientation changes:
The selector fixCameraOrientation contains Journeyman's code with one extra case, wrapped in a check to make sure the sourceType is the camera:
The important case here is the case where the device orientation goes to portrait. The overlay's view needs to be reset in this case. It's also important for the fixCameraOrientation selector to be called after the image picker view loads, in case the device is rotated:
The UIImagePickerController has a property called cameraViewTransform. Applying a CGAffineTransform to this will transform the preview image but will not transform the captured image which will therefore be correctly captured. I have the same problem that you describe and I solved it (for iOS7) by creating my camera controller and placing it in a popover as follows:
I also scale the image when in Landscape so that it fills the viewfinder more than it otherwise would. To my mind this is all rather nasty, but I will hopefully be able to remove it once iOS7.1 arrives.