在我的仅横向iPhone应用程序中,我启动了一个UIImagePickerController来拍摄照片,但是从相机显示的实时图像是纵向的,周围有空白区域。图像旋转。
按下相机按钮后,预览非常混乱,大部分预览关闭,并且视图未正确对齐。
Apple承认这是缺陷,正在努力。
我的问题是,是否有人通过合法(合法或非法)解决方案让我现在能够正常工作。我不会通过非法修复发布到App Store,但我会有一个更好的用户测试应用程序 - 目前相机在风景中几乎无法使用。
如果可以的话,我会附上一个简单的测试项目和图像。
编辑 - 只是为了澄清,我得到的图像是正确的风景。我想要相机&预览用户界面看起来正确!
答案 0 :(得分:88)
答案比你想象的更荒谬。我遇到了同样的问题,并在某个论坛找到了解决方案。将拍摄的图像传递给这样的方法:
// 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;
}
:(
答案 1 :(得分:1)
我认为您根本不需要额外的工作来处理imageRotation或EXIF数据。图像drawInRect将自动处理。
所以,你只需要获得这个尺寸或图像并将其重新绘制到一个新图像,这就足够了。
答案 2 :(得分:1)
我不想在捕获后旋转图像;我希望在横向模式下正确显示预览。所以在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;
});
}];
}
有点痛苦,但它确实适用于照片和视频。图像选择器的控件以纵向模式显示,但应用程序的其余部分仅为横向显示。
答案 3 :(得分:1)
我通过使UIImagePickerController
以全屏模式显示来解决了这个问题,这也是Apple推荐用于iPad的。
来自UIImagePickerController
documentation:
在iPad上,如果指定源类型为UIImagePickerControllerSourceTypeCamera,则可以模态(全屏)或使用弹出窗口显示图像选择器。但是,Apple建议您仅以全屏方式显示相机界面。
答案 4 :(得分:0)
您需要将自定义视图(工具栏和标题)设置为cameraOverlayView
,您还需要将allowsEditing
和showsCameraControls
设置为NO
将隐藏标准控件和预览。
这就是我在应用程序中发现的必要(我认为我需要选择器是纵向的,但是如果用户将其设备旋转到横向,我需要它来应用一些ui更改。)
如果需要,可以提供代码。
P.S。我的代码中仍然存在一些错误,但我正在研究它:)
答案 5 :(得分:0)
可以通过如下将modalPresentationStyle
设置为overCurrentContext
来解决此问题,
picker.modalPresentationStyle = .overCurrentContext
并在Main Thread
上展示您的图片选择器,
DispatchQueue.main.async {
self.present(picker, animated: true) {
}
}
为什么会这样?
因为ImagePickerController
仅在纵向模式下有效,并且当您尝试从风景视图控制器显示选取器时,尝试将状态栏设置为纵向模式。因此,如果将modalPresentationStyle
设置为overCurrentContext
,则它将不会尝试设置方向。它将考虑当前方向。
请参见Apple guide line中的ImagePickerController
。它说,
UIImagePickerController类仅支持纵向模式。该类旨在按原样使用,不支持子类化。此类的视图层次结构是私有的,除了一个例外,不得修改。您可以将自定义视图分配给cameraOverlayView属性,并使用该视图显示其他信息或管理摄像头界面与您的代码之间的交互。
答案 6 :(得分:0)
让我知道是否不鼓励重新发布上面的最佳答案,我为Alex Wayne的答案准备了2019年快速版本。
/**
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
}