我的应用程序中也有类似的情况。然而,预览在 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:
添加条件以验证视图实际上在屏幕上并确保应该显示相机if (self.view.window && self.sourceType == UIImagePickerControllerSourceTypeCamera)
。如果是这样,请设置起始方向self.startingOrientation = [UIApplication sharedApplication].statusBarOrientation;
。
添加与上面相同的-statusBarOrientationDidChange:
条件,但这次如果为真,我们将更新相机变换。首先,我们根据初始旋转获得偏移旋转。当您输入UIImagePickerController
纵向以外的方向时,这是需要的。
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
纵向模式然后旋转到横向,预览的顶部和底部会有黑条。对此的解决方案不是缩放,而是删除超级视图的剪辑。我们只需要尝试一次,所以首先检查我们是否调用了这段代码。还要确保我们只在旋转后才调用此代码。如果它在初始方向被调用,它不会立即工作。
if (!self.didAttemptToRemoveCropping && self.startingOrientation != [UIApplication sharedApplication].statusBarOrientation) {
self.didAttemptToRemoveCropping = YES;
[self findClippedSubviewInView:self.view];
}
最后在代码中,-findClippedSubviewInView:
我们遍历所有子视图以搜索带有.clipsToBounds = YES
. 如果那是真的,我们再做一个条件来验证它的祖先超级视图之一是正确的。
for (UIView* subview in view.subviews) {
if (subview.clipsToBounds) {
if ([self hasAncestorCameraView:subview]) {
subview.clipsToBounds = NO;
break;
}
}
[self findClippedSubviewInView:subview];
}
在中,-hasAncestorCameraView:
我们简单地循环超级视图链,如果其中一个类CameraView
在名称中,则返回 true。
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