MFMailComposeViewController图像方向

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

我使用imagePickerController拍照,然后使用MFMailComposeViewController作为附件发送。 所有这一切正在工作。

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

如果以横向模式拍摄照片,MFMailComposeViewController将以横向模式显示照片,并在到达目的地电子邮件地址时以横向模式显示。 所以这一切都OK。

我的两个testing设备都有同样的问题。 一个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并使用主摄像头以3264×2448进行拍摄,以保持简单。 但是,此问题会影响其他设备和解决scheme。

解决这个问题的关键是要意识到,当你拍摄一张图像时,在任何风景的肖像中,iPhone总是以相同的方式存储UIImage:3264宽和2448高。

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

 UIImageOrientation orient = image.imageOrientation; 

请注意,orientation属性并不描述UIImage中的数据是如何物理configuration的(如3264w x 2448h)。 它只是描述了图像被捕获时的方向。

该属性的用途是告诉即将显示图像的软件,如何旋转它以使其正确显示。

如果您以纵向模式捕捉图像,则image.imageOrientation将返回UIImageOrientationRight。 这就告诉显示软件需要将图像旋转90度,以便正确显示。 要清楚的是,这个“旋转”不会影响UIImage的底层存储,它仍然是3264w x 2448h。

如果以横向模式捕捉图像,则image.imageOrientation将返回UIImageOrientationUp。 UIImageOrientationUp告诉显示软件的图像是好的显示,因为它是; 不需要旋转。 再次,UIIMage的底层存储是3264w x 2448h。

一旦你清楚了数据如何物理存储与定向属性如何被用来描述它在捕获时的取向之间的区别,事情就会变得更加清晰。

我创build了几行debugging代码来“查看”所有这些。

下面是添加了debugging代码的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-Mailng MFMailComposeViewController代码,那么我在其中添加了debugging代码:

 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代码中一样的值。

让我们看看问题的具体performance:

开始时,相机拍摄人像,并以竖线模式正确显示:

 [[self imageView] setImage: gImag]; // send image to screen 

它显示正确,因为这行代码看到方向属性,并适当地旋转图像(而不是触摸底层存储在3264×2448)。

控制stream程现在转到电子邮件代码,方向属性仍然存在于gImag中,因此当MFMailComposeViewController代码在传出电子邮件中显示图像时,它的方向是正确的。 物理图像仍然保存为3264×2448。

发送电子邮件,并在接收端,方向属性的知识已经丢失,所以接收软件显示的图像,因为它是物理布局为3264×2448,即风景。

在debugging时,我遇到了一个额外的困惑。 这就是说,如果你不正确地复制它的方向属性可以从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的debugging数据时,我们得到了UIImageOrientationUp和width = 3264和height = 2448。

方向属性被剥离,默认方向为“向上”。 如果你不知道剥离正在进行,它可以真正混淆事情。

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

在imagePickerController代码中的东西是不变的; 图像像以前一样被捕获。

Flow-of-control转到电子邮件代码,但现在方向属性已从tmp图像中删除,因此当MFMailComposeViewController代码在传出电子邮件中显示tmp图像时,将以横向模式显示(因为默认方向是UIImageOrientationUp,所以没有旋转的3264×2448图像)。

电子邮件被发送,并在接收端,方向属性的知识也丢失,所以接收软件显示的图像,因为它是物理布局为3264×2448,即风景。

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

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

这样可以避免沿途丢失方位属性,但是当您通过电子邮件发送图像时,仍然无法处理远端的损失。

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

我在这里find了一些我已经整合到我的程序中的代码。 该代码将根据其方向属性物理旋转底层存储的图像。

对于一个方向为UIImageOrientationRight的UIImage,它将物理地旋转UIImage,使其最终成为2448×3264,并将剥离方向属性,从而将其视为默认的UIImageOrientationUp。

对于UIImageOrientationUp方向的UIImage,它什么都不做。 它让睡觉的风景狗躺下。

如果你这样做,那么我认为(基于迄今为止我所看到的),UIImage的方向属性是多余的。 只要它遗失/剥离或设置为UIImageOrientationUp,当显示embedded在电子邮件中的图像时,您的图像应该在沿途和远处的每一步都正确显示。

我在答案中所讨论的每一件事情,我都亲身一步步地看着它发生。

所以,在这里我的最终代码工作:

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

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

 - (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 ); } 

欢呼,从新西兰。

我一直在iOS SDK7的图像取向上挣扎,我希望在这个讨论中增加一些内容,希望对其他人有所帮助。 UIImageJPEGRepresentation工作正常,但请注意,使用UIImagePNGRepresentation可能会导致方向不正确的图像。

我正在开发一个应用程序,使用设备相机拍摄照片后上传。 我暂时不把图像存储到磁盘,而是直接发送到服务器。

将UIImage呈现给NSData时,可以在UIImagePNGRepresentationUIImageJPEGRepresentation之间进行select。

使用UIImagePNGRepresentation ,以纵向和横向拍摄的照片都会生成横向图像。 在这种情况下,肖像图像逆时针旋转90度。

使用UIImageJPEGRepresentation ,纵向和横向图像的结果都是正确的。

规模部分的迅捷版本:

 class func scaleAndRotateImageUsingOrientation(imageIn : UIImage) -> UIImage { //takes into account the stored rotation on the image fromt he camera and makes it upright let kMaxResolution = 3264; // Or whatever let imgRef = imageIn.CGImage let width = CGImageGetWidth(imgRef) let height = CGImageGetHeight(imgRef) var transform = CGAffineTransformIdentity var bounds = CGRectMake( 0.0, 0.0, CGFloat(width), CGFloat(height) ) if ( width > kMaxResolution || height > kMaxResolution ) { let ratio : CGFloat = CGFloat(width) / CGFloat(height); if (ratio > 1) { bounds.size.width = CGFloat(kMaxResolution) bounds.size.height = bounds.size.width / ratio; } else { bounds.size.height = CGFloat(kMaxResolution) bounds.size.width = bounds.size.height * ratio } } let scaleRatio : CGFloat = bounds.size.width / CGFloat(width) var imageSize = CGSizeMake( CGFloat(CGImageGetWidth(imgRef)), CGFloat(CGImageGetHeight(imgRef)) ) let orient = imageIn.imageOrientation; var boundHeight : CGFloat = 0.0 switch(orient) { case UIImageOrientation.Up: //EXIF = 1 transform = CGAffineTransformIdentity; break; case UIImageOrientation.UpMirrored: //EXIF = 2 transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0); transform = CGAffineTransformScale(transform, -1.0, 1.0); break; case UIImageOrientation.Down: //EXIF = 3 transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height); transform = CGAffineTransformRotate(transform, CGFloat(M_PI)); break; case UIImageOrientation.DownMirrored: //EXIF = 4 transform = CGAffineTransformMakeTranslation(0.0, imageSize.height); transform = CGAffineTransformScale(transform, 1.0, -1.0); break; case UIImageOrientation.LeftMirrored: //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 * CGFloat(M_PI) / 2.0); break; case UIImageOrientation.Left: //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 * CGFloat(M_PI) / 2.0); break; case UIImageOrientation.RightMirrored: //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, CGFloat(M_PI) / 2.0); break; case UIImageOrientation.Right: //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, CGFloat(M_PI) / 2.0); break; default: break } UIGraphicsBeginImageContext( bounds.size ); var context = UIGraphicsGetCurrentContext(); if ( orient == UIImageOrientation.Right || orient == UIImageOrientation.Left ) { CGContextScaleCTM(context, -scaleRatio, scaleRatio); CGContextTranslateCTM(context, CGFloat(-height), 0); } else { CGContextScaleCTM(context, scaleRatio, -scaleRatio); CGContextTranslateCTM(context, 0, CGFloat(-height)); } CGContextConcatCTM( context, transform ); CGContextDrawImage( UIGraphicsGetCurrentContext(), CGRectMake( 0, 0, CGFloat(width), CGFloat(height) ), imgRef ); let imageCopy = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return( imageCopy ); } 

我认为你可以得到UIImage API自动处理旋转,而不需要手动做转换。

UIImage方法的大小drawInRect的文档都说他们把方向考虑在内,所以我把UIImage绘制到一个新的上下文中,并从那里抓取结果(自动旋转的)图像。 这里是一个类别中的代码:

 @interface UIImage (Orientation) - (UIImage*)imageAdjustedForOrientation; @end @implementation UIImage (Orientation) - (UIImage*)imageAdjustedForOrientation { // The UIImage methods size and drawInRect take into account // the value of its imageOrientation property // so the rendered image is rotated as necessary. UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale); [self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)]; UIImage *orientedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return orientedImage; } @end 

我刚刚find了这个更好的答案 , 这个答案几乎是一样的,但是如果方向已经是正确的话,还包括不重新绘制图像的优化。

您需要根据其方向旋转图像。 这里有一个技巧,如何轻松地做到这一点:

 NSData *data = UIImagePNGRepresentation(sourceImage); UIImage *tmp = [UIImage imageWithData:data]; UIImage *fixed = [UIImage imageWithCGImage:tmp.CGImage scale:sourceImage.scale orientation:sourceImage.imageOrientation];