一旦按下相机按钮,预览就会变得非常混乱,大部分预览都超出了屏幕范围,并且视图没有正确对齐。
苹果已经承认这是一个缺陷,并正在解决它。
我的问题是,是否有人有一个(合法或非法的)解决方法,可以让我现在就让它工作?我不会使用非法修复方法发布到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;
}
:(
UIImagePickerController
以全屏模式显示来解决了这个问题,这也是苹果公司推荐的iPad上的做法。UIImagePickerController
文档中得知:
在iPad上,如果指定了UIImagePickerControllerSourceTypeCamera源类型,则可以使用全屏模态方式或使用弹出窗口呈现图像选择器。但是,苹果公司建议仅以全屏方式呈现相机界面。
我认为你根本不需要额外的工作来处理图像旋转或EXIF数据。Image drawInRect会自动处理这些问题。
因此,你只需要获取图像的大小并将其重新绘制到新图像中即可。
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;
});
}];
}
如果重新发布上面的最佳答案是不被鼓励的,请让我知道,我有一个针对Alex Wayne答案的2019年swift版本。
/**
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
}
你需要设置自定义视图(包括工具栏和标题)作为cameraOverlayView
,同时你需要将allowsEditing
和showsCameraControls
设置为NO
,这样会隐藏标准控件和预览。
这是我在制作应用程序时发现的必要条件(尽管我需要选择器处于纵向状态,但如果用户将设备旋转到横向状态,我需要应用一些UI更改)。
如果需要,可以提供代码。
P.S. 我的代码仍然存在一些错误,但我正在努力解决 :)
modalPresentationStyle
设置为overCurrentContext
来解决,如下所示: picker.modalPresentationStyle = .overCurrentContext
并在Main Thread
上展示您的图像选择器,例如:
DispatchQueue.main.async {
self.present(picker, animated: true) {
}
}
为什么会发生这种情况?
因为 ImagePickerController
只在竖屏模式下工作,当您尝试从横屏 viewController 中呈现 picker 时,它会尝试将您的状态栏设置为竖屏模式。因此,如果您将 modalPresentationStyle
设置为 overCurrentContext
,则它将不会尝试设置方向。它将考虑当前方向。
请参阅 Apple 指南 获取有关 ImagePickerController
的更多信息。它说明:
UIImagePickerController 类仅支持纵向模式。该类旨在按原样使用,并且不支持子类化。此类的视图层次结构是私有的,不能进行修改,只有一种例外情况。您可以将自定义视图分配给 cameraOverlayView 属性,并使用该视图呈现其他信息或管理相机界面与您的代码之间的交互。