iphone - MFMailComposeViewController 图像方向

标签 iphone image email ios6 orientation

我正在开发一个通用应用程序,我正在为 iOS6 编码。

我正在使用 imagePickerController 拍摄照片,然后使用 MFMailComposeViewController 将其作为附件发送。所有这些都在起作用。

我的问题是,当我以纵向模式拍摄照片时,MFMailComposeViewController 以横向模式显示它。此外,当它到达目的地电子邮件地址时,它以横向模式显示。

如果我以横向模式拍摄图片,它会在横向模式下由 MFMailComposeViewController 显示,当它到达目标电子邮件地址时,它以横向模式显示。所以没关系。

我的两个测试设备都有同样的问题;一个 iPhone5 和一个 iPad2。

如何使以人像模式拍摄的照片以人像模式到达电子邮件目的地?

这是我将图像添加到电子邮件的方法:

if ( [MFMailComposeViewController canSendMail] )
   {
   MFMailComposeViewController * mailVC = [MFMailComposeViewController new];
   NSArray * aAddr  = [NSArray arrayWithObjects: gAddr, nil];
   NSData * imageAsNSData = UIImagePNGRepresentation( gImag );

   [mailVC setMailComposeDelegate: self];
   [mailVC        setToRecipients: aAddr];
   [mailVC             setSubject: gSubj];
   [mailVC      addAttachmentData: imageAsNSData
                         mimeType: @"image/png"
                         fileName: @"myPhoto.png"];
   [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 在相机上以纵向模式拍摄图像并将该图像传递给 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 x 2448h。

一旦您清楚数据的物理存储方式与方向属性在捕获时如何用于描述其方向之间的区别,事情就会开始变得更加清晰。

我创建了几行调试代码来“查看”所有这些。

这是添加了调试代码的 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 和 width = 3264 和 height = 2448 到达。

如果我们拍摄风景,gImage 以 UIImageOrientationUp 和 width = 3264 和 height = 2448 到达。

如果我们继续阅读 E-Mailng 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,则可以从 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 和 width = 3264 和 height = 2448。

方向属性被剥离,默认方向是向上。如果你不知道剥离正在进行,它真的会混淆事情。

如果我运行此代码,我现在得到以下结果:

imagePickerController 代码中的内容没有改变;像以前一样捕获图像。

控制流继续到电子邮件程序代码,但现在方向属性已从 tmp 图像中剥离,因此当 MFMailComposeViewController 代码在传出电子邮件中显示 tmp 图像时,它以横向模式显示(因为默认方向是 UIImageOrientationUp 所以没有旋转 3264x2448 图像)。

电子邮件被发送,在接收端,方向属性的知识也丢失了,因此接收软件将图像显示为物理布局为 3264x2448,即横向。

在制作 UIImage 副本时,可以避免这种对方向属性的“剥离”,如果知道它正在发生,可以使用以下代码制作 UIImage 副本:
UIImage * tmp = [UIImage imageWithCGImage: gImag.CGImage
                                    scale: gImag.scale
                              orientation: gImag.imageOrientation];

这将允许您避免沿途丢失方向属性,但当您通过电子邮件发送图像时,它仍然不会处理远端的丢失。

有比所有这些搞乱和担心方向属性更好的方法。

我找到了一些代码 here我已经集成到我的程序中。此代码将根据其方向属性物理旋转底层存储的图像。

对于方向为 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


if ( [MFMailComposeViewController canSendMail] )
   {
   MFMailComposeViewController * mailVC = [MFMailComposeViewController new];
   NSArray                     * aAddr  = [NSArray arrayWithObjects: gAddr, nil];

   //...lets not touch the original UIImage

   UIImage * tmpImag = [UIImage imageWithCGImage: gImag.CGImage
                                           scale: gImag.scale
                                     orientation: gImag.imageOrientation];

   //...do physical rotation, if needed

   PIMG ImgOut = [gU scaleAndRotateImage: tmpImag];

   //...note orientation is UIImageOrientationUp now

   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." );   

最后,这是我从 here 中获取的代码如有必要,进行物理旋转:
- (UIImage *) scaleAndRotateImage: (UIImage *) imageIn
   //...thx: http://blog.logichigh.com/2008/06/05/uiimage-fix/
   {
   int kMaxResolution = 3264; // Or whatever

   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:                                        //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 );
   }

干杯,来自新西兰。

关于iphone - MFMailComposeViewController 图像方向,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20204495/

相关文章:

iphone - Iphone 应用程序的表格边框

iphone - 启动电话应用程序以调用没有号码的电话

java - 我可以使用 UrlEncodedFormEntity 为多个部分上传图像和文本吗?

vb.net 发送带有图像的电子邮件

email - 如何模拟软弹跳情况进行测试?

iphone - 如何正确保存到 coredata 一对多关系

iphone - 核心动画还是 OpenGL ES?

javascript - 使用 Javascript 更改 IMG SRC

python - 如何从 Python 中的像素值列表创建 PNG 图像文件?

html - 是否可以将 BMP 转换为 CSS/HTML