1. 程式人生 > >使用MFMailComposeViewController在iOS應用內編輯郵件使用方法及常見問題

使用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存在的時候,更會出現奇葩問題,,還需小心謹慎!!!