iPad iOS7 – UIPopoverController中的UIImagePickerController有错误的预览图像

我正在UIPopoverController内使用UIImagePickerController,这是与iOS6完美工作。 使用iOS 7时,显示捕捉图像的“预览”图像会旋转,但是如果我拍摄照片,则会正确保存。

这是我如何得到我的select器:

UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init]; imagePicker.delegate = self; imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; imagePicker.mediaTypes = [NSArray arrayWithObjects: (NSString *) kUTTypeImage, nil]; imagePicker.allowsEditing = NO; 

并将其添加到popover控制器:

 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]; 

这些是在UIScrollView的button位置的计算,以显示在正确的位置popover:

 presentPopoverFromRect:CGRectMake(aPosViewA.x, cameraButton_y, 100.0, 30.0) 

我不认为这个问题在那里,因为我已经尝试了几种组合。

我也尝试在全屏模式下捕捉图像,但应用程序只允许使用横向模式。 如果图像是以纵向模式拍摄的,而模式视图则被解散,那么应用程序也会保持纵向模式。 我无法find一种方法来防止UIImagePickerController切换到肖像模式或强制应用程序回到横向模式,如果模态视图被解散。

UPDATE

我已经从这里得到了答案,并进一步。

在创buildselect器之后,在显示popup窗口之前,我转换视图:

 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; } 

只要我不转过iPad,它就可以工作。 为此,我正在注册改变方向的事件:

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

并更改选取器视图:

 - (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; } } } 

仍然存在的问题:正如我一开始写的那样,照片拍摄的时候,正确地接受或者解除了照片。 这现在也转变了。 不知何故,我需要知道什么时候拍摄图像并将其转换回来。

而且,这真是一个讨厌的黑客攻击,可能不会用于下一个iOS更新。 有没有人有一个想法如何以更清洁的方式来实现?

更新2

这太讨厌了,我find了一个更清晰的解决scheme,它解决了我的问题,但不是解决popover控制器中图像select器最初的问题的答案,这是苹果不推荐的,但允许的。

我现在已经分类了UIImagePickerController像这样:

 @implementation QPImagePickerController - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { return UIInterfaceOrientationIsLandscape(toInterfaceOrientation); } - (BOOL)shouldAutorotate { return YES; } - (NSUInteger)supportedInterfaceOrientations{ return UIInterfaceOrientationMaskLandscape; } @end 

我在全屏使用imagepicker而不是popover。 目前在iOS7中进行了testing。

UIImagePickerController有一个名为cameraViewTransform的属性。 将CGAffineTransform应用于此将会转换预览图像,但不会转换捕捉的图像,因此将被正确捕捉。 我有同样的问题,你描述,我解决了它(iOS7)通过创build我的相机控制器,并将其放置在一个popover如下:

 UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init]; [imagePickerController setDelegate:self]; imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera; CGFloat scaleFactor=1.3f; switch ([UIApplication sharedApplication].statusBarOrientation) { case UIInterfaceOrientationLandscapeLeft: imagePickerController.cameraViewTransform = CGAffineTransformScale(CGAffineTransformMakeRotation(M_PI * 90 / 180.0), scaleFactor, scaleFactor); break; case UIInterfaceOrientationLandscapeRight: imagePickerController.cameraViewTransform = CGAffineTransformScale(CGAffineTransformMakeRotation(M_PI * -90 / 180.0), scaleFactor, scaleFactor); break; case UIInterfaceOrientationPortraitUpsideDown: imagePickerController.cameraViewTransform = CGAffineTransformMakeRotation(M_PI * 180 / 180.0); break; default: break; } __popoverController = [[UIPopoverController alloc] initWithContentViewController:imagePickerController]; [__popoverController presentPopoverFromRect:presentationRect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; 

我在横向时也会缩放图像,所以它比其他方式填满取景器更多。 在我看来,这一切都相当讨厌,但我希望能够在iOS7.1到来时将其删除。

我发现了另一种解决scheme,它处理的是UIIMagePickerView根据Journeyman提供的答案呈现设备的情况。 它还处理视图从UIOrientationLandscapeRight / UIOrientationLandscapeLeft旋转回到UIOrientationPortrait的情况。

我创build了UIImagePickerController的一个子类:

 #import <UIKit/UIKit.h> @interface PMImagePickerController : UIImagePickerController @end 

然后将其注册为在设备方向更改时收到通知:

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

select器fixCameraOrientation包含Journeyman的代码和一个额外的案例,包装在一个检查,以确保sourceType是相机:

 - (void)fixCameraOrientation:(NSNotification*)notification { if (self.sourceType == UIImagePickerControllerSourceTypeCamera) { CGFloat scaleFactor=1.3f; switch ([UIApplication sharedApplication].statusBarOrientation) { case UIInterfaceOrientationLandscapeLeft: self.cameraViewTransform = CGAffineTransformScale(CGAffineTransformMakeRotation(M_PI * 90 / 180.0), scaleFactor, scaleFactor); break; case UIInterfaceOrientationLandscapeRight: self.cameraViewTransform = CGAffineTransformScale(CGAffineTransformMakeRotation(M_PI * -90 / 180.0), scaleFactor, scaleFactor); break; case UIInterfaceOrientationPortraitUpsideDown: self.cameraViewTransform = CGAffineTransformMakeRotation(M_PI * 180 / 180.0); break; case UIInterfaceOrientationPortrait: self.cameraViewTransform = CGAffineTransformIdentity; break; default: break; } } } 

这里的重要情况是设备朝向纵向的情况。 在这种情况下,重叠视图需要重置。 在旋转设备的情况下,加载图像select器视图之后调用fixCameraOrientationselect器也很重要:

 - (void)viewDidLoad { [super viewDidLoad]; [self fixCameraOrientation:nil]; } 

我的应用程序中有类似的情况。 但是,预览在iOS7中正确旋转,而不是在iOS8中旋转。 此代码假定您有多个方向。

首先是UIImagePickerController

从顶部开始,将#import <AVFoundation/AVFoundation.h>到.m文件。

还要添加一个属性来保存初始方向@property (nonatomic) UIInterfaceOrientation startingOrientation; 另一个用于删除剪辑的条件@property (nonatomic) BOOL didAttemptToRemoveCropping;

我们要听几个通知。 UIApplicationDidChangeStatusBarOrientationNotification显然是侦听设备旋转。 当相机开始捕捉时, AVCaptureSessionDidStartRunningNotification被调用。

 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarOrientationDidChange:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(captureSessionDidStart:) name:AVCaptureSessionDidStartRunningNotification object:nil]; 

-captureSessionDidStart:添加一个条件来validation视图实际上是在屏幕上,并确保相机应该显示if (self.view.window && self.sourceType == UIImagePickerControllerSourceTypeCamera) 。 如果是这样,请设置开始方向self.startingOrientation = [UIApplication sharedApplication].statusBarOrientation;

-statusBarOrientationDidChange:添加与上面相同的条件,但是这次如果是true,我们将更新相机变换。 首先我们得到基于初始旋转的偏移旋转。 当您以纵向方式inputUIImagePickerController时,这是需要的。

 CGFloat startingRotation = ({ CGFloat rotation; switch (self.startingOrientation) { case UIInterfaceOrientationPortraitUpsideDown: rotation = M_PI; break; case UIInterfaceOrientationLandscapeLeft: rotation = -M_PI_2; break; case UIInterfaceOrientationLandscapeRight: rotation = M_PI_2; break; default: rotation = 0.0f; break; } rotation; }); 

接下来我们将更新当前旋转的相机变换。

 self.cameraViewTransform = CGAffineTransformMakeRotation(({ CGFloat angle; switch ([UIApplication sharedApplication].statusBarOrientation) { case UIInterfaceOrientationPortraitUpsideDown: angle = startingRotation + M_PI; break; case UIInterfaceOrientationLandscapeLeft: angle = startingRotation + M_PI_2; break; case UIInterfaceOrientationLandscapeRight: angle = startingRotation + -M_PI_2; break; default: angle = startingRotation; break; } angle; })); 

最后,我们将尝试从开始的方向中删除以90度方向呈现的黑条。 (这可能只是一个iOS8的问题。)稍微更详细一点,如果我以纵向模式进入UIImagePickerController ,然后旋转到横向,预览的顶部和底部会出现黑条。 这个解决scheme不是按比例缩放,而是去除超级视图的裁剪。 我们只需要尝试一次,所以首先检查我们是否调用了这个代码。 另外请确保我们只调用这个代码,如果我们已经旋转。 如果它在最初的方向调用它不会马上工作。

 if (!self.didAttemptToRemoveCropping && self.startingOrientation != [UIApplication sharedApplication].statusBarOrientation) { self.didAttemptToRemoveCropping = YES; [self findClippedSubviewInView:self.view]; } 

最后在-findClippedSubviewInView:的代码中-findClippedSubviewInView:我们遍历所有的子视图来search.clipsToBounds = YES的视图。 如果这是真的,我们再做一个条件来validation其祖传观点之一是正确的。

 for (UIView* subview in view.subviews) { if (subview.clipsToBounds) { if ([self hasAncestorCameraView:subview]) { subview.clipsToBounds = NO; break; } } [self findClippedSubviewInView:subview]; } 

-hasAncestorCameraView:我们简单地循环超视图链,并返回true,如果其中一个类名称中有CameraView

 if (view == self.view) { return NO; } NSString* className = NSStringFromClass([view class]); if ([className rangeOfString:@"CameraView"].location != NSNotFound) { return YES; } else { return [self hasAncestorCameraView:view.superview]; } 

这就是代码的细分,这是所有的一起。

 #import <AVFoundation/AVFoundation.h> #import "GImagePickerController.h" @interface GImagePickerController () @property (nonatomic) UIInterfaceOrientation startingOrientation; @property (nonatomic) BOOL didAttemptToRemoveCropping; @end @implementation GImagePickerController - (instancetype)init { self = [super init]; if (self) { if ([[[UIDevice currentDevice] systemVersion] intValue] >= 8) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarOrientationDidChange:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(captureSessionDidStart:) name:AVCaptureSessionDidStartRunningNotification object:nil]; } } return self; } - (void)dealloc { if ([[[UIDevice currentDevice] systemVersion] intValue] >= 8) { [[NSNotificationCenter defaultCenter] removeObserver:self]; } } #pragma mark - Capture Session - (void)captureSessionDidStart:(NSNotification *)notification { if (self.view.window && self.sourceType == UIImagePickerControllerSourceTypeCamera) { [self updateStartingOrientation]; } } #pragma mark - Orientation - (void)updateStartingOrientation { self.startingOrientation = [UIApplication sharedApplication].statusBarOrientation; [self updateCameraTransform]; } - (void)updateCameraTransform { CGFloat startingRotation = ({ CGFloat rotation; switch (self.startingOrientation) { case UIInterfaceOrientationPortraitUpsideDown: rotation = M_PI; break; case UIInterfaceOrientationLandscapeLeft: rotation = -M_PI_2; break; case UIInterfaceOrientationLandscapeRight: rotation = M_PI_2; break; default: rotation = 0.0f; break; } rotation; }); self.cameraViewTransform = CGAffineTransformMakeRotation(({ CGFloat angle; switch ([UIApplication sharedApplication].statusBarOrientation) { case UIInterfaceOrientationPortraitUpsideDown: angle = startingRotation + M_PI; break; case UIInterfaceOrientationLandscapeLeft: angle = startingRotation + M_PI_2; break; case UIInterfaceOrientationLandscapeRight: angle = startingRotation + -M_PI_2; break; default: angle = startingRotation; break; } angle; })); if (!self.didAttemptToRemoveCropping && self.startingOrientation != [UIApplication sharedApplication].statusBarOrientation) { self.didAttemptToRemoveCropping = YES; [self findClippedSubviewInView:self.view]; } } - (void)statusBarOrientationDidChange:(NSNotification *)notification { if (self.view.window && self.sourceType == UIImagePickerControllerSourceTypeCamera) { [self updateCameraTransform]; } } #pragma mark - Remove Clip To Bounds - (BOOL)hasAncestorCameraView:(UIView *)view { if (view == self.view) { return NO; } NSString* className = NSStringFromClass([view class]); if ([className rangeOfString:@"CameraView"].location != NSNotFound) { return YES; } else { return [self hasAncestorCameraView:view.superview]; } } - (void)findClippedSubviewInView:(UIView *)view { for (UIView* subview in view.subviews) { if (subview.clipsToBounds) { if ([self hasAncestorCameraView:subview]) { subview.clipsToBounds = NO; break; } } [self findClippedSubviewInView:subview]; } } @end 

带相机的iPad – 不要在popup窗口中显示。 相反,在模态视图控制器,就像你在iPhone上。 (至less从iOS 7开始)