我花了几个小时的时间解决了这个问题,现在我清楚了发生了什么以及如何修复它。
再次强调,我遇到的问题是:
当我使用imagePickerController以纵向模式在相机上拍摄图像并将其传递给MFMailComposeViewController发送邮件时,在目标电子邮件地址接收后,它会以横向模式不正确地显示。
但是,如果我使用横向模式拍摄图片然后发送它,那么在电子邮件的目的地中正确地显示
那么,我该如何使以纵向模式拍摄的图片以纵向模式到达电子邮件目的地呢?那是我的最初问题。
以下是代码,就像我在原始问题中展示的一样,只是现在我将图像作为JPEG发送而不是PNG,但这没有任何区别。
这是我使用imagePickerController捕获图像并将其放入名为gImag的全局变量中的方法:
gImag = (UIImage *)[info valueForKey: UIImagePickerControllerOriginalImage];
[[self imageView] setImage: gImag]; // send image to screen
[self imagePickerControllerRelease: picker ]; // free the picker object
这是我使用MFMailComposeViewController发送电子邮件的方法:
if ( [MFMailComposeViewController canSendMail] )
{
MFMailComposeViewController * mailVC = [MFMailComposeViewController new];
NSArray * aAddr = [NSArray arrayWithObjects: gAddr, nil];
NSData * imageAsNSData = UIImageJPEGRepresentation( gImag );
[mailVC setMailComposeDelegate: self];
[mailVC setToRecipients: aAddr];
[mailVC setSubject: gSubj];
[mailVC addAttachmentData: imageAsNSData
mimeType: @"image/jpg"
fileName: @"myPhoto.jpg"];
[mailVC setMessageBody: @"Blah blah"
isHTML: NO];
[self presentViewController: mailVC
animated: YES
completion: nil];
}
else NSLog( @"Device is unable to send email in its current state." );
在我讲解如何解决这个问题时,我将专注于使用iPhone 5的主摄像头拍摄,该摄像头以3264x2448的分辨率进行拍摄,以保持简单。但是,这个问题也会影响其他设备和分辨率。
解决这个问题的关键是要认识到,当你拍摄一张图像时,无论是竖屏还是横屏,iPhone始终以相同的方式存储UIImage: 宽度为3264,高度为2448。
UIImage有一个描述其捕获时方向的属性,可以像这样获取它:
UIImageOrientation orient = image.imageOrientation
注意,orientation属性并不描述UIImage中数据的物理配置(例如3264w x 2448h);它仅描述在捕获图像时的方向。该属性的作用是告诉即将显示图像的软件如何旋转它以使其正确显示。
如果您以肖像模式捕获图像,则image.imageOrientation将返回UIImageOrientationRight。这告诉显示软件需要将图像旋转90度以便正确显示。请注意,“旋转”不会影响UIImage的基础存储方式,其仍为3264w x 2448h。
如果您以横向模式捕获图像,则image.imageOrientation将返回UIImageOrientationUp。UIImageOrientationUp告诉显示软件可以按原样显示图像,无需旋转。同样,UIIMage的基础存储方式为3264w x2448h。
一旦您明确了数据的物理存储方式与方向属性用于描述捕获时的方向之间的区别,事情就开始变得更加清晰。
我创建了几行调试代码来“查看”所有这些内容。
以下是添加了调试代码的imagePickerController代码:
gImag = (PIMG)[info valueForKey: UIImagePickerControllerOriginalImage];
UIImageOrientation orient = gImag.imageOrientation;
CGFloat width = CGImageGetWidth(gImag.CGImage);
CGFloat height = CGImageGetHeight(gImag.CGImage);
[[self imageView] setImage: gImag]; // send image to screen
[self imagePickerControllerRelease: picker ]; // free the picker object
如果我们拍摄肖像,gImage将以UIImageOrientationRight形式到达,宽度为3264,高度为2448。
如果我们拍摄横向照片,则gImage以UIImageOrientationUp形式到达,宽度为3264,高度为2448。
如果我们继续进行E-Mail发送的MFMailComposeViewController代码,我也在其中添加了调试代码:
if ( [MFMailComposeViewController canSendMail] )
{
MFMailComposeViewController * mailVC = [MFMailComposeViewController new];
NSArray * aAddr = [NSArray arrayWithObjects: gAddr, nil];
UIImageOrientation orient = gImag.imageOrientation;
CGFloat width = CGImageGetWidth(gImag.CGImage);
CGFloat height = CGImageGetHeight(gImag.CGImage);
NSData * imageAsNSData = UIImageJPEGRepresentation( gImag );
[mailVC setMailComposeDelegate: self];
[mailVC setToRecipients: aAddr];
[mailVC setSubject: gSubj];
[mailVC addAttachmentData: imageAsNSData
mimeType: @"image/jpg"
fileName: @"myPhoto.jpg"];
[mailVC setMessageBody: @"Blah blah"
isHTML: NO];
[self presentViewController: mailVC
animated: YES
completion: nil];
}
else NSLog( @"Device is unable to send email in its current state." );
这里没有什么特别的。我们得到的数值与我们在imagePickerController代码中得到的数值完全相同。
让我们看看问题具体表现在哪里:
首先,相机拍摄一张肖像照片,在竖屏模式下正确显示该照片,代码如下:
[[self imageView] setImage: gImag]; // send image to screen
由于这行代码查看了方向属性并适当地旋转了图像(同时不触及底层存储在3264x2448上),因此它会正确地显示。
现在流程控制进入电子邮件程序代码,gImag中仍存在方向属性,因此当MFMailComposeViewController代码在发送的电子邮件中显示图像时,它将被正确定向。物理图像仍存储为3264x2448。
邮件已发送,在接收端,方向属性的信息已丢失,因此接收软件按照物理布局显示图像,即横向3264x2448。
在调试过程中,我遇到了另一个问题。如果你不正确地复制UIImage,则可能会剥离方向属性。下面的代码展示了这个问题:
if ( [MFMailComposeViewController canSendMail] )
{
MFMailComposeViewController * mailVC = [MFMailComposeViewController new];
NSArray * aAddr = [NSArray arrayWithObjects: gAddr, nil];
UIImageOrientation orient = gImag.imageOrientation;
CGFloat width = CGImageGetWidth(gImag.CGImage);
CGFloat height = CGImageGetHeight(gImag.CGImage);
UIImage * tmp = [[UIImage alloc] initWithCGImage: gImag.CGImage];
orient = tmp.imageOrientation;
width = CGImageGetWidth(tmp.CGImage);
height = CGImageGetHeight(tmp.CGImage);
NSData * imageAsNSData = UIImageJPEGRepresentation( tmp );
[mailVC setMailComposeDelegate: self];
[mailVC setToRecipients: aAddr];
[mailVC setSubject: gSubj];
[mailVC addAttachmentData: imageAsNSData
mimeType: @"image/jpg"
fileName: @"myPhoto.jpg"];
[mailVC setMessageBody: @"Blah blah"
isHTML: NO];
[self presentViewController: mailVC
animated: YES
completion: nil];
}
else NSLog( @"Device is unable to send email in its current state." );
当我们查看新UIImage tmp的调试数据时,我们得到UIImageOrientationUp、宽度为3264和高度为2448。
方向属性被剥离了, 默认方向是Up。 如果你不知道正在发生的剥离,它可能会使事情变得混乱。
如果我运行这段代码,我现在会得到以下结果:
在imagePickerController代码中的事情没有改变; 图像仍然像以前一样被捕获。
控制流程继续进行到E-Mailer代码,但现在方向属性已经从tmp图像中剥离掉了,因此当MFMailComposeViewController代码在外发邮件中显示tmp图像时,它显示为横向模式(因为默认方向是UIImageOrientationUp,所以3264x2448图像没有旋转)。
邮件发送成功后,在接收端,方向属性的信息也丢失了,因此接收软件将图像按照物理布局3264x2448显示,即横向。
如果想避免在复制UIImage时剥离方向属性,则可以使用以下代码来制作UIImage副本:
UIImage * tmp = [UIImage imageWithCGImage: gImag.CGImage
scale: gImag.scale
orientation: gImag.imageOrientation];
那将允许您避免在途中丢失方向属性,但仍无法解决电子邮件中图像的丢失。
有一种比这些繁琐和担心方向属性的更好的方法。
我在这里找到了一些代码,并将其集成到我的程序中。该代码将根据其方向属性实际旋转底层存储的图像。
对于具有UIImageOrientationRight方向的UIImage,它将物理旋转UIImage,使其最终为2448x3264,并剥离方向属性,因此随后以默认的UIImageOrientationUp显示。
对于具有UIImageOrientationUp方向的UIImage,不做任何处理。它让睡觉的景观狗躺下。
如果您这样做(基于我迄今为止看到的内容),那么之后UIImage的方向属性就是多余的。只要它保持缺失/剥离或设置为UIImageOrientationUp,则每个步骤中正确显示您的图像,并在远程端显示嵌入您的电子邮件中的图像。
我在答案中讨论的所有内容都是我个人单步执行并观察的。
所以,这是我的最终代码:
gImag = (PIMG)[info valueForKey: UIImagePickerControllerOriginalImage];
[[self imageView] setImage: gImag]; // send image to screen
[self imagePickerControllerRelease: picker ]; // free the picker object
and
if ( [MFMailComposeViewController canSendMail] )
{
MFMailComposeViewController * mailVC = [MFMailComposeViewController new];
NSArray * aAddr = [NSArray arrayWithObjects: gAddr, nil];
UIImage * tmpImag = [UIImage imageWithCGImage: gImag.CGImage
scale: gImag.scale
orientation: gImag.imageOrientation];
PIMG ImgOut = [gU scaleAndRotateImage: tmpImag];
NSData * imageAsNSData = UIImageJPEGRepresentation( ImgOut, 0.9f );
[mailVC setMailComposeDelegate: self];
[mailVC setToRecipients: aAddr];
[mailVC setSubject: gSubj];
[mailVC addAttachmentData: imageAsNSData
mimeType: @"image/jpg"
fileName: @"myPhoto.jpg"];
[mailVC setMessageBody: @"Blah blah"
isHTML: NO];
[self presentViewController: mailVC
animated: YES
completion: nil];
}
else NSLog( @"Device is unable to send email in its current state." );
最后,这是我从这里获取的代码,如果需要物理旋转,则进行旋转:
- (UIImage *) scaleAndRotateImage: (UIImage *) imageIn
{
int kMaxResolution = 3264;
CGImageRef imgRef = imageIn.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 = bounds.size.width / ratio;
}
else
{
bounds.size.height = kMaxResolution;
bounds.size.width = bounds.size.height * ratio;
}
}
CGFloat scaleRatio = bounds.size.width / width;
CGSize imageSize = CGSizeMake( CGImageGetWidth(imgRef), CGImageGetHeight(imgRef) );
UIImageOrientation orient = imageIn.imageOrientation;
CGFloat boundHeight;
switch(orient)
{
case UIImageOrientationUp:
transform = CGAffineTransformIdentity;
break;
case UIImageOrientationUpMirrored:
transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
transform = CGAffineTransformScale(transform, -1.0, 1.0);
break;
case UIImageOrientationDown:
transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;
case UIImageOrientationDownMirrored:
transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
transform = CGAffineTransformScale(transform, 1.0, -1.0);
break;
case UIImageOrientationLeftMirrored:
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:
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:
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:
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 );
}
来自新西兰的问候。