Ответ 1
Я потратил несколько часов на работу над этой проблемой, и теперь я понимаю, что происходит и как это исправить.
Повторяю, проблема, с которой я столкнулся, это:
Когда я снимаю изображение в портретном режиме на камере с помощью 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
И вот как я E-Mail с помощью 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 высоко.
A UIImage обладает свойством, которое описывает его ориентацию во время захвата изображения, и вы можете получить его так:
UIImageOrientation orient = image.imageOrientation;
Обратите внимание, что свойство ориентации не описывает, как физически настроены данные в 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 и шириной = 3264 и height = 2448.
Если мы снимаем пейзаж, gImage поступает с UIImageOrientationUp и width = 3264 и height = 2448.
Если мы перейдем к коду E-Mailng MFMailComposeViewController, Ive также добавил туда код отладки:
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).
Теперь поток управления переходит к коду E-Mailer, и свойство ориентации все еще присутствует в 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." );
Когда мы смотрим на данные отладки для нового UMLmage tmp, получаем UIImageOrientationUp и width = 3264 и height = 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];
Это позволит вам избежать потери свойства ориентации на этом пути, но по-прежнему не будет иметь дело с его потерей в дальнем конце при отправке по электронной почте изображения.
Theres лучший способ, чем все это возиться и беспокоиться о свойстве ориентации.
Я нашел код здесь, что Ive интегрирован в мою программу. Этот код будет физически вращать базовое сохраненное изображение в соответствии со свойством ориентации.
Для UIImage с ориентацией UIImageOrientationRight он будет физически вращать UIImage, чтобы он заканчивался как 2448x3264, и он отделяет свойство ориентации, поэтому он рассматривается как UIImageOrientationUp по умолчанию.
Для UIImage с ориентацией UIImageOrientationUp он ничего не делает. Это позволяет лгать собакам спящего ландшафта.
Если вы сделаете это, я думаю (на основе того, что я видел до сих пор), что свойство ориентации UIImage является излишним после этого. До тех пор, пока он остается отсутствующим или разделенным или установленным на UIImageOrientationUp, ваши изображения должны отображаться правильно на каждом шаге на этом пути и на дальнем конце, когда отображаются изображения, встроенные в ваш E-Mail.
Все, что я обсуждал в ответе, я лично одношаговый и смотрел, как это происходит.
Итак, вот мой последний код, который работает:
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 );
}
Приветствия, из Новой Зеландии.