在我的仅横向iPhone应用程序中,我启动了一个UIImagePickerController来拍摄照片,但是从相机显示的实时图像是纵向的,周围有空白区域。图像旋转。
按下相机按钮后,预览非常混乱,大部分预览关闭屏幕,并且视图未正确对齐。
Apple承认这是缺陷,并正在努力。
我的问题是,是否有人有一个解决方法(合法或非法),这将使我现在能够正常工作。我不会通过非法修复发布到App Store,但我会有一个更好的应用程序用于用户测试 - 目前相机在景观中几乎无法使用。
如果可以的话,我会附上一个简单的测试项目和图像。
编辑 - 只是为了澄清,我得到的图像是正确的风景。我希望相机和预览UI看起来正确!
答案比你想象的更荒谬。我遇到了同样的问题,并在某个论坛找到了解决方案。将拍摄的图像传递到如下方法:
// Code from: http://discussions.apple.com/thread.jspa?messageID=7949889
- (UIImage *)scaleAndRotateImage:(UIImage *)image {
int kMaxResolution = 640; // Or whatever
CGImageRef imgRef = image.CGImage;
CGFloat width = CGImageGetWidth(imgRef);
CGFloat height = CGImageGetHeight(imgRef);
CGAffineTransform transform = CGAffineTransformIdentity;
CGRect bounds = CGRectMake(0, 0, width, height);
if (width > kMaxResolution || height > kMaxResolution) {
CGFloat ratio = width/height;
if (ratio > 1) {
bounds.size.width = kMaxResolution;
bounds.size.height = roundf(bounds.size.width / ratio);
}
else {
bounds.size.height = kMaxResolution;
bounds.size.width = roundf(bounds.size.height * ratio);
}
}
CGFloat scaleRatio = bounds.size.width / width;
CGSize imageSize = CGSizeMake(CGImageGetWidth(imgRef), CGImageGetHeight(imgRef));
CGFloat boundHeight;
UIImageOrientation orient = image.imageOrientation;
switch(orient) {
case UIImageOrientationUp: //EXIF = 1
transform = CGAffineTransformIdentity;
break;
case UIImageOrientationUpMirrored: //EXIF = 2
transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
transform = CGAffineTransformScale(transform, -1.0, 1.0);
break;
case UIImageOrientationDown: //EXIF = 3
transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;
case UIImageOrientationDownMirrored: //EXIF = 4
transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
transform = CGAffineTransformScale(transform, 1.0, -1.0);
break;
case UIImageOrientationLeftMirrored: //EXIF = 5
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width);
transform = CGAffineTransformScale(transform, -1.0, 1.0);
transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
break;
case UIImageOrientationLeft: //EXIF = 6
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeTranslation(0.0, imageSize.width);
transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
break;
case UIImageOrientationRightMirrored: //EXIF = 7
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeScale(-1.0, 1.0);
transform = CGAffineTransformRotate(transform, M_PI / 2.0);
break;
case UIImageOrientationRight: //EXIF = 8
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
transform = CGAffineTransformRotate(transform, M_PI / 2.0);
break;
default:
[NSException raise:NSInternalInconsistencyException format:@"Invalid image orientation"];
}
UIGraphicsBeginImageContext(bounds.size);
CGContextRef context = UIGraphicsGetCurrentContext();
if (orient == UIImageOrientationRight || orient == UIImageOrientationLeft) {
CGContextScaleCTM(context, -scaleRatio, scaleRatio);
CGContextTranslateCTM(context, -height, 0);
}
else {
CGContextScaleCTM(context, scaleRatio, -scaleRatio);
CGContextTranslateCTM(context, 0, -height);
}
CGContextConcatCTM(context, transform);
CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef);
UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return imageCopy;
}
:(
我认为您根本不需要额外的工作来处理imageRotation或EXIF数据。图像drawInRect将自动处理。
因此,您只需要获取此大小或图像并将其重新绘制为新图像,这就足够了。
我不想在捕获后旋转图像;我希望在横向模式下正确显示预览。所以在iOS 6中,我在应用程序级别允许纵向模式,但是将应用程序的根视图控制器设置为类MyNonrotatingNavigationController
,定义如下:
@implementation MyNonrotatingNavigationController
-(NSUInteger) supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
@end
因此,在此导航控制器中显示的所有内容都将处于横向(您可以使用任何视图控制器执行此操作)。现在,当我需要显示图像选择器时,我将应用程序窗口的根视图控制器替换为支持纵向模式的通用控制器。为了防止旧的根视图控制器及其视图解除分配,我保留指向它们的指针,直到我准备将它们放回应用程序窗口。
#define APP_DELEGATE ((MyAppDelegate*)[[UIApplication sharedApplication] delegate])
static UIViewController *pickerContainer = nil;
static UIViewController *oldRoot = nil;
static UIView *holdView = nil;
-(void) showPicker
{
...create UIImagePickerController...
oldRoot = APP_DELEGATE.window.rootViewController;
holdView = [[UIView alloc] initWithFrame:APP_DELEGATE.window.bounds];
[holdView addSubview:oldRoot.view];
pickerContainer = [UIViewController new];
pickerContainer.view = [[UIView alloc] initWithFrame:APP_DELEGATE.window.bounds];
APP_DELEGATE.window.rootViewController = pickerContainer;
[pickerContainer presentViewController:picker animated:YES completion:NULL];
}
-(void) imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info
{
[pickerContainer dismissViewControllerAnimated:YES completion:^{
dispatch_async( dispatch_get_main_queue(), ^{
APP_DELEGATE.window.rootViewController = oldRoot;
[APP_DELEGATE.window addSubview:oldRoot.view];
pickerContainer = nil;
oldRoot = nil;
holdView = nil;
});
}];
}
有点痛苦,但它似乎适用于照片和视频。图像选择器的控件以纵向模式显示,但应用程序的其余部分仅为横向。
我通过使UIImagePickerController
以全屏模式出现来解决了这个问题,这也是Apple推荐的iPad。
来自UIImagePickerController
documentation:
在iPad上,如果指定源类型为UIImagePickerControllerSourceTypeCamera,则可以模态(全屏)或使用弹出窗口显示图像选择器。但是,Apple建议您仅全屏显示相机界面。
您需要将自定义视图(带有工具栏和标题)设置为cameraOverlayView
,您还需要将allowsEditing
和showsCameraControls
设置为NO
,因为这会隐藏标准控件和预览。这就是我在应用程序中发现的必要(我认为我需要选择器是纵向的,但如果用户将其设备旋转到横向,我需要它来应用一些ui更改)。
如果需要,可以提供代码。
附:我的代码中仍然存在一些错误,但我正在努力:)
这个问题可以通过将modalPresentationStyle
设置为overCurrentContext
来解决,如下所示,
picker.modalPresentationStyle = .overCurrentContext
并在Main Thread
上展示您的图像选择器,
DispatchQueue.main.async {
self.present(picker, animated: true) {
}
}
为什么会这样?
因为ImagePickerController
仅在纵向模式下工作,当您尝试从横向视图控制器呈现选择器时,它会尝试将您的状态栏设置为纵向模式。所以,如果你将modalPresentationStyle
设置为overCurrentContext
,那么它将不会尝试设置方向。它将考虑当前的方向。
看Apple guide line为ImagePickerController
。它指出,
UIImagePickerController类仅支持纵向模式。此类旨在按原样使用,不支持子类化。此类的视图层次结构是私有的,不得修改,但有一个例外。您可以将自定义视图分配给cameraOverlayView属性,并使用该视图显示其他信息或管理相机界面与代码之间的交互。
让我知道如果不鼓励重新发布上面的最佳答案,我有一个2019年的快速版本为Alex Wayne的答案。
/**
Scale and rotate a UIImage so that it is correctly oriented
:param: image: The image to be rotated
:returns: UIImage
*/
func scaleAndRotateImage(_ image: UIImage) -> UIImage {
let kMaxResolution: CGFloat = 640
let imgRef: CGImage = image.cgImage!
let width: CGFloat = CGFloat(imgRef.width)
let height: CGFloat = CGFloat(imgRef.height)
var transform: CGAffineTransform = CGAffineTransform.identity
var bounds: CGRect = CGRect(x: 0, y: 0, width: width, height: height)
if width > kMaxResolution || height > kMaxResolution {
let ratio: CGFloat = width / height
if ratio > 1 {
bounds.size.width = kMaxResolution
bounds.size.height = (bounds.size.width / ratio)
}
else {
bounds.size.height = kMaxResolution
bounds.size.width = (bounds.size.height * ratio)
}
}
let scaleRatio: CGFloat = bounds.size.width / width
let imageSize: CGSize = CGSize(width: CGFloat(imgRef.width), height: CGFloat(imgRef.height))
var boundHeight: CGFloat
let orient: UIImage.Orientation = image.imageOrientation
switch orient {
case UIImageOrientation.up:
transform = CGAffineTransform.identity
case UIImageOrientation.upMirrored:
transform = CGAffineTransform(translationX: imageSize.width, y: 0.0)
transform = transform.scaledBy(x: -1.0, y: 1.0)
case UIImageOrientation.down:
transform = CGAffineTransform(translationX: imageSize.width, y: imageSize.height)
transform = transform.rotated(by: CGFloat(Double.pi))
case UIImageOrientation.downMirrored:
transform = CGAffineTransform(translationX: 0.0, y: imageSize.height)
transform = transform.scaledBy(x: 1.0, y: -1.0)
case UIImageOrientation.leftMirrored:
boundHeight = bounds.size.height
bounds.size.height = bounds.size.width
bounds.size.width = boundHeight
transform = CGAffineTransform(translationX: imageSize.height, y: imageSize.width)
transform = transform.scaledBy(x: -1.0, y: 1.0)
transform = transform.rotated(by: CGFloat(3.0 * .pi / 2.0))
case UIImageOrientation.left:
boundHeight = bounds.size.height
bounds.size.height = bounds.size.width
bounds.size.width = boundHeight
transform = CGAffineTransform(translationX: 0.0, y: imageSize.width)
transform = transform.rotated(by: CGFloat(3.0 * .pi / 2.0))
case UIImageOrientation.rightMirrored:
boundHeight = bounds.size.height
bounds.size.height = bounds.size.width
bounds.size.width = boundHeight
transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
transform = transform.rotated(by: CGFloat(.pi / 2.0))
case UIImageOrientation.right:
boundHeight = bounds.size.height
bounds.size.height = bounds.size.width
bounds.size.width = boundHeight
transform = CGAffineTransform(translationX: imageSize.height, y: 0.0)
transform = transform.rotated(by: CGFloat(.pi / 2.0))
}
UIGraphicsBeginImageContext(bounds.size)
let context: CGContext = UIGraphicsGetCurrentContext()!
if orient == UIImage.Orientation.right || orient == UIImage.Orientation.left {
context.scaleBy(x: -scaleRatio, y: scaleRatio)
context.translateBy(x: -height, y: 0)
}
else {
context.scaleBy(x: scaleRatio, y: -scaleRatio)
context.translateBy(x: 0, y: -height)
}
context.concatenate(transform)
UIGraphicsGetCurrentContext()?.draw(imgRef, in: CGRect(x: 0, y: 0, width: width, height: height))
let imageCopy: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return imageCopy
}