Я провел несколько часов работы по этому вопросу, и я теперь ясно, о том, что происходит и как это исправить.
Повторим, проблема я столкнулся это:
Когда я снимать изображение в портретном режиме на камеру с помощью 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;
Обратите внимание, что свойство ориентации делает не описывают, как данные в 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 и высота = 2448.
Если мы снимаем пейзаж, gImage поступает с UIImageOrientationUp и шириной = 3264 и высотой = 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).
Теперь поток управления переходит к коду 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.");
Когда мы посмотрим на данные отладки для нового 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];
Это позволит вам избежать потери свойства ориентации на этом пути, но все равно не будет иметь дело с его потерей на дальнем конце при отправке по электронной почте изображения.
Есть лучший способ, чем все это возиться и беспокоиться о свойстве ориентации.
Я нашел код here, который я включил в свою программу. Этот код будет физически вращать базовое сохраненное изображение в соответствии со свойством ориентации.
Для 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.");
И, наконец, вот код, который я взял из 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);
}
Cheers, from New Зеландия.
Очень полный и полезный ответ, спасибо, я собираюсь попытаться реализовать это сейчас. Просто FYI - вы можете отметить это как ответ. – Greg
Спасибо, Грег. Я намеревался это сделать, а потом забыл вернуться к нему. – Gallymon
Работал отлично! в отличие от многих других решений, которые я прочитал и попробовал. Благодаря! – Nate