使用MFMailComposeViewController在iOS應用內編輯郵件使用方法及常見問題
Tips:
在APP中傳送郵件,是一個很普遍的應用場景,譬如對於APP的使用者反饋,就可以通過在APP中直接編輯郵件或者開啟iOS自帶的Mail來實現郵件反饋。下面先回顧一下在APP中使用郵件的兩種方式,然後再和大家分享一個專案中遇到的問題。
iOS系統框架提供的兩種傳送Email的方法:openURL 和 MFMailComposeViewController:
Type 1:openURL
openURL呼叫系統Mail客戶端 是我們在iOS3之前實現發郵件功能的主要方法。效果是,從A應用切換到Mail,實際是在Mail中編輯傳送郵件,這種方法是很不友好的。
下面是詳細程式碼:
-(void)launchMailApp
{
NSMutableString *mailUrl = [[[NSMutableString alloc]init]autorelease];
//新增收件人
NSArray *toRecipients = [NSArray arrayWithObject: @"[email protected]"];
[mailUrl appendFormat:@"mailto:%@", [toRecipients componentsJoinedByString:@","]] ;
//新增抄送
NSArray *ccRecipients = [NSArray arrayWithObjects:@"[email protected]", @"[email protected]", nil];
[mailUrl appendFormat:@"?cc=%@", [ccRecipients componentsJoinedByString:@","]];
//新增密送
NSArray *bccRecipients = [NSArray arrayWithObjects:@"[email protected]" , nil];
[mailUrl appendFormat:@"&bcc=%@", [bccRecipients componentsJoinedByString:@","]];
//新增主題
[mailUrl appendString:@"&subject=my email"];
//新增郵件內容
[mailUrl appendString:@"&body=<b>email</b> body!"];
NSString* email = [mailUrl stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
[[UIApplication sharedApplication] openURL: [NSURL URLWithString:email]];
}
Type 2:MFMailComposeViewController
MFMailComposeViewController是iOS3之後新增的,源於MessageUI.framework。顯而易見,我們通過MFMailComposeViewController這個控制器,可以在我們自己的APP中展現一個郵件編輯頁面,這樣傳送郵件就不需要離開當前的APP了。前提是裝置中的Mail要添加了賬戶,或者iCloud設定了郵件賬戶。所以需要MFMailComposeViewController提供的canSendMail判斷是否已繫結賬戶。
MFMailComposeViewController使用前的準備:
1、專案中引入MessageUI.framework
2、匯入MFMailComposeViewController.h
3、遵循MFMailComposeViewControllerDelegate,並實現代理方法來處理髮送
下面是詳細程式碼:
- (void)displayMailPicker
{
MFMailComposeViewController *mailPicker = [[MFMailComposeViewController alloc] init];
mailPicker.mailComposeDelegate = self;
//設定主題
[mailPicker setSubject: @"eMail主題"];
//新增收件人
NSArray *toRecipients = [NSArray arrayWithObject: @"[email protected]"];
[mailPicker setToRecipients: toRecipients];
//新增抄送
NSArray *ccRecipients = [NSArray arrayWithObjects:@"[email protected]", @"[email protected]", nil];
[mailPicker setCcRecipients:ccRecipients];
//新增密送
NSArray *bccRecipients = [NSArray arrayWithObjects:@"[email protected]", nil];
[mailPicker setBccRecipients:bccRecipients];
// 新增一張圖片
UIImage *addPic = [UIImage imageNamed: @"[email protected]"];
NSData *imageData = UIImagePNGRepresentation(addPic); // png
//關於mimeType:http://www.iana.org/assignments/media-types/index.html
[mailPicker addAttachmentData: imageData mimeType: @"" fileName: @"Icon.png"];
//新增一個pdf附件
NSString *file = [self fullBundlePathFromRelativePath:@"abc.pdf"];
NSData *pdf = [NSData dataWithContentsOfFile:file];
[mailPicker addAttachmentData: pdf mimeType: @"" fileName: @"abc.pdf"];
NSString *emailBody = @"<font color='red'>eMail</font> 正文";
[mailPicker setMessageBody:emailBody isHTML:YES];
[self presentModalViewController: mailPicker animated:YES];
}
#pragma mark - 實現 MFMailComposeViewControllerDelegate
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
//關閉郵件傳送視窗
[self dismissModalViewControllerAnimated:YES];
NSString *msg;
switch (result) { // result是列舉型別
case MFMailComposeResultCancelled:
msg = @"使用者取消編輯郵件";
break;
case MFMailComposeResultSaved:
msg = @"使用者成功儲存郵件";
break;
case MFMailComposeResultSent:
msg = @"使用者點擊發送,將郵件放到佇列中,還沒傳送";
break;
case MFMailComposeResultFailed:
msg = @"使用者試圖儲存或者傳送郵件失敗";
break;
default:
msg = @"";
break;
}
[self alertWithMessage:msg];
}
目前,使用iOS系統郵件風格的發郵件方式,是對上面兩種的結合:
- (void)sendMailWithAddress:(NSArray *)address
{
if ([MFMailComposeViewController canSendMail]) {
[self displayMailPicker]; //APP內編輯郵件
}
else {
[self launchMailApp]; // 開啟Mail客戶端,編輯郵件
}
}
Type 3:開源SMTP Client
https://github.com/jetseven/skpsmtpmessage
它的優點是我們不必侷限於iOS系統風格的郵件編輯頁面,而可以根據自己APP的UI風格定製相應的檢視以適應整體的設計
在SKPSMTPMessage類中,並沒有對檢視進行任何的要求,它提供的都是資料層級的處理,你只需要定義好相應的傳送要求就可以實現郵件傳送了。至於是以什麼樣的方式獲取這些資訊,就可以根據軟體的需求來確定互動方式和檢視樣式了
下面是示例程式碼:
SKPSMTPMessage *testMsg = [[SKPSMTPMessage alloc] init];
testMsg.fromEmail = @"[email protected]";
testMsg.toEmail =@"[email protected]";
testMsg.relayHost = @"smtp.gmail.com";
testMsg.requiresAuth = YES;
testMsg.login = @"[email protected]";
testMsg.pass = @"test";
testMsg.subject = [NSString stringWithCString:"測試" encoding:NSUTF8StringEncoding];
testMsg.bccEmail = @"[email protected]";
testMsg.wantsSecure = YES; // smtp.gmail.com doesn't work without TLS!
// Only do this for self-signed certs!
// testMsg.validateSSLChain = NO;
testMsg.delegate = self;
NSDictionary *plainPart = [NSDictionary
dictionaryWithObjectsAndKeys:@"text/plain",kSKPSMTPPartContentTypeKey,
[NSString stringWithCString:"測試正文" encoding:NSUTF8StringEncoding],
kSKPSMTPPartMessageKey,@"8bit",kSKPSMTPPartContentTransferEncodingKey,nil];
NSString *vcfPath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"vcf"];
NSData *vcfData = [NSData dataWithContentsOfFile:vcfPath];
NSDictionary *vcfPart = [NSDictionary dictionaryWithObjectsAndKeys: @"text/directory;\r\n\tx-unix-mode=0644;\r\n\tname=\"test.vcf\"",kSKPSMTPPartContentTypeKey,
@"attachment;\r\n\tfilename=\"test.vcf\"",kSKPSMTPPartContentDispositionKey,
[vcfData encodeBase64ForData],kSKPSMTPPartMessageKey,@"base64",kSKPSMTPPartContentTransferEncodingKey,nil];
testMsg.parts = [NSArray arrayWithObjects:plainPart,vcfPart,nil];
[testMsg send];
上面就是APP傳送郵件的幾種方式,那麼最近我碰到的坑是什麼意思呢?我用了type 1和type 2結合的方式,先描述一下這個bug:
1、iOS7的pad,繫結郵箱賬戶(Mail和iCloud隨便一個),能用type 2開啟APP內編輯頁面
2、iOS7的pad,不繫結郵箱賬戶,能用type 1開啟Mail客戶端
3、iOS8、9的pad,繫結郵箱賬戶(Mail和iCloud隨便一個),無任何反應
4、iOS8、9的pad,不繫結郵箱賬戶,能用type 1開啟Mail客戶端
是MFMailComposeViewController在iOS系統版本上有bug嗎?後來在stackoverflow上看到,MFMailComposeViewController在父控制器上還有其他檢視操作的時候 確實存在著問題,比如我的情況:
[[RootViewController sharedRootViewController] sendMailWithAddress:[mailAddress componentsSeparatedByString:@";"]]; //1
[[RootViewController sharedRootViewController] dismissPopoverWithAnimation:YES]; // 2
第1行就是RootViewController推出MFMailComposeViewController的方法
第2行是RootViewController dismiss掉另一個popoverViewController的方法
那麼問題來了,當我發出命令,RootViewController推出MFMailComposeViewController之後,MFMailComposeViewController出現之前,RootViewController又去dismissVC,這就是在搞事情了,造成混亂,導致MFMailComposeViewController還沒appear就被dismiss掉了。
問題找到了就好辦了,將1、2位置互換:
[[RootViewController sharedRootViewController] dismissPopoverWithAnimation:YES];
[[RootViewController sharedRootViewController] sendMailWithAddress:[mailAddress componentsSeparatedByString:@";"]];
提前將該dismiss的執行了,讓推出MFMailComposeViewController之後,儘量避免檢視和VC上的變化。
總之,出現這問題,就要檢查推出MFMailComposeViewController之後,父控制器上有無其他檢視方面的操作。
其實習慣就好,在navigationbar存在的時候,更會出現奇葩問題,,還需小心謹慎!!!