MFMailComposeViewController图像方向

Gal*_*mon 13 email iphone image orientation ios6

我正在开发一个通用应用程序,我正在为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." );
   }
Run Code Online (Sandbox Code Playgroud)

Gal*_*mon 56

我已经花了几个小时研究这个问题,现在我已经清楚发生了什么以及如何解决它.

重复一下,我遇到的问题是:

当我使用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
Run Code Online (Sandbox Code Playgroud)

这就是我使用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." );
Run Code Online (Sandbox Code Playgroud)

当我描述如何解决这个问题时,我将专注于使用iPhone 5并使用主摄像头拍摄3264x2448以保持简单.但是,此问题会影响其他设备和分辨率.

解决这个问题的关键是要意识到,当您拍摄图像时,无论是横向画像,iPhone总是以相同的方式存储UIImage:3264宽和2448高.

UIImage有一个属性,描述了捕获图像时的方向,你可以这样得到它:

UIImageOrientation orient = image.imageOrientation;
Run Code Online (Sandbox Code Playgroud)

注意,取向性也没有描述如何在UIImage的数据被物理地配置(如作为3264瓦特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
Run Code Online (Sandbox Code Playgroud)

如果我们拍摄肖像,gImage会以UIImageOrientationRight到达,宽度= 3264,高度= 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." );
Run Code Online (Sandbox Code Playgroud)

在这里看到没什么了不起的.我们得到的值与imagePickerController代码中的值完全相同.

让我们看看问题究竟是如何表现出来的:

首先,相机拍摄人像照片,并以纵向模式正确显示:

[[self imageView] setImage: gImag];                 // send image to screen
Run Code Online (Sandbox Code Playgroud)

它正确显示,因为这行代码可以看到orientation属性并适当地旋转图像(而不是在3264x2448处触及底层存储).

控制流现在转到E-Mailer代码,并且orientation属性仍然存在于gImag中,因此当MFMailComposeViewController代码在传出电子邮件中显示图像时,它是正确定向的.物理图像仍然存储为3264x2448.

发送电子邮件,并且在接收端,方向属性的知识已丢失,因此接收软件显示图像,因为它实际布局为3264x2448,即横向.

在调试中,我遇到了额外的困惑.也就是说,如果你不正确地复制了它,那么可以从UIImage中去除orientation属性.

此代码显示了问题:

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." );
Run Code Online (Sandbox Code Playgroud)

当我们查看新UIImage tmp的调试数据时,我们得到UIImageOrientationUp和width = 3264以及height = 2448.

方向属性已被剥离,默认方向为"向上".如果你不知道正在进行剥离,它确实会让事情变得混乱.

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

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

控制流程继续进行E-Mailer代码,但现在已从tmp图像中删除了orientation属性,因此当MFMailComposeViewController代码在传出电子邮件中显示tmp图像时,它将以横向模式显示(因为默认方向是UIImageOrientationUp,因此没有完成3264x2448图像的旋转).

发送电子邮件,并且在接收端,还缺少对方向属性的知识,因此接收软件在物理布局为3264x2448(即横向)时显示图像.

通过使用以下代码制作UIImage副本,可以避免在制作UIImage副本时"取消"方向属性,如果有人知道它正在进行中,则:

UIImage * tmp = [UIImage imageWithCGImage: gImag.CGImage
                                    scale: gImag.scale
                              orientation: gImag.imageOrientation];
Run Code Online (Sandbox Code Playgroud)

这样可以避免在途中丢失方向属性,但是当您通过电子邮件发送图像时,它仍然无法解决远端丢失的问题.

有一种更好的方式比所有这些搞乱和担心方向属性.

我在这里找到了一些代码,我已将其集成到我的程序中.此代码将根据其方向属性物理旋转基础存储图像.

对于具有UIImageOrientationRight方向的UIImage,它将物理旋转UIImage,使其最终为2448x3264,它将去除orientation属性,以便此后将其视为默认的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
Run Code Online (Sandbox Code Playgroud)

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." );   
Run Code Online (Sandbox Code Playgroud)

最后,这里是我从这里抓取的代码,如果需要的话,进行物理旋转:

- (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 );
   }
Run Code Online (Sandbox Code Playgroud)

干杯,来自新西兰.

  • @Oskariagon,我为我提供的代码示例中的PIMG和gU道歉.我使用了很多定义来在我的代码中创建缩写,以使代码更紧凑,并且(对我来说)更具可读性.因此,PIMG代表UIImage*.在我发布之前我以为我已经把所有这些都搞砸了但是我错过了这个.并且gU指向一个全局可见的对象,其中包含我从我的代码中调用的实用程序例程. (4认同)