我有模拟器中的Swift(iOS8)MFMailComposeViewController真正的误解
我创build一个CSV文件,并尝试通过电子邮件发送。 显示发送邮件的窗口,但没有填充电子邮件的正文,也没有附加文件。 应用程序挂起这个屏幕:
button“取消”不起作用。 在控制台出现几秒钟后:
viewServiceDidTerminateWithError: Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 "The operation couldn't be completed. (_UIViewServiceInterfaceErrorDomain error 3.)" UserInfo=0x7f8409f29b50 {Message=Service Connection Interrupted} <MFMailComposeRemoteViewController: 0x7f8409c89470> timed out waiting for fence barrier from com.apple.MailCompositionService
有我的代码:
func actionSheet(actionSheet: UIActionSheet!, clickedButtonAtIndex buttonIndex: Int) { if buttonIndex == 0 { println("Export!") var csvString = NSMutableString() csvString.appendString("Date;Time;Systolic;Diastolic;Pulse") for tempValue in results { //result define outside this function var tempDateTime = NSDate() tempDateTime = tempValue.datePress var dateFormatter = NSDateFormatter() dateFormatter.dateFormat = "dd-MM-yyyy" var tempDate = dateFormatter.stringFromDate(tempDateTime) dateFormatter.dateFormat = "HH:mm:ss" var tempTime = dateFormatter.stringFromDate(tempDateTime) csvString.appendString("\n\(tempDate);\(tempTime);\(tempValue.sisPress);\(tempValue.diaPress);\(tempValue.hbPress)") } let fileManager = (NSFileManager.defaultManager()) let directorys : [String]? = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory,NSSearchPathDomainMask.AllDomainsMask, true) as? [String] if ((directorys) != nil) { let directories:[String] = directorys!; let dictionary = directories[0]; let plistfile = "bpmonitor.csv" let plistpath = dictionary.stringByAppendingPathComponent(plistfile); println("\(plistpath)") csvString.writeToFile(plistpath, atomically: true, encoding: NSUTF8StringEncoding, error: nil) var testData: NSData = NSData(contentsOfFile: plistpath) var myMail: MFMailComposeViewController = MFMailComposeViewController() if(MFMailComposeViewController.canSendMail()){ myMail = MFMailComposeViewController() myMail.mailComposeDelegate = self // set the subject myMail.setSubject("My report") //Add some text to the message body var sentfrom = "Mail sent from BPMonitor" myMail.setMessageBody(sentfrom, isHTML: true) myMail.addAttachmentData(testData, mimeType: "text/csv", fileName: "bpmonitor.csv") //Display the view controller self.presentViewController(myMail, animated: true, completion: nil) } else { var alert = UIAlertController(title: "Alert", message: "Your device cannot send emails", preferredStyle: UIAlertControllerStyle.Alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil)) self.presentViewController(alert, animated: true, completion: nil) } } else { println("File system error!") } } }
试着改用UIActivityViewController
发送邮件:
let fileURL: NSURL = NSURL(fileURLWithPath: plistpath) let actViewController = UIActivityViewController(activityItems: [fileURL], applicationActivities: nil) self.presentViewController(actViewController, animated: true, completion: nil)
看到大致相同的屏幕发送电子邮件,一段时间后返回到前一个屏幕。 在控制台,现在另一个错误:
viewServiceDidTerminateWithError: Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 "The operation couldn't be completed. (_UIViewServiceInterfaceErrorDomain error 3.)" UserInfo=0x7faab3296ad0 {Message=Service Connection Interrupted} Errors encountered while discovering extensions: Error Domain=PlugInKit Code=13 "query cancelled" UserInfo=0x7faab3005890 {NSLocalizedDescription=query cancelled} <MFMailComposeRemoteViewController: 0x7faab3147dc0> timed out waiting for fence barrier from com.apple.MailCompositionService
有关于PlugInKit
东西。
尝试使用UIDocumentInteractionController
而不是UIActivityViewController
:
let docController = UIDocumentInteractionController(URL: fileURL) docController.delegate = self docController.presentPreviewAnimated(true) ... func documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController!) -> UIViewController! { return self }
我看到这个屏幕的内容CSV文件: http : //prntscr.com/4ilgax我按下右上angular的button导出,看到这个屏幕http://prntscr.com/4ilguk我在哪里select邮件和几秒钟我看到http://prntscr.com/4ilh2h然后返回到显示文件的内容! 在控制台中使用与使用UIActivityViewController
时相同的消息。
* *重要 – 请勿使用此模拟器。 * *
即使在2016年,模拟器也不支持从应用程序发送邮件。
事实上,模拟器根本就没有邮件客户端。
但! 看到底部的消息!
亨利给出了完整的答案。 你必须
– 在早期阶段分配并启动MFMailComposeViewController
– 把它放在一个静态variables中 ,然后,
– 只要需要,获取静态MFMailComposeViewController实例并使用它。
而且你几乎可以肯定必须在每次使用后循环全局MFMailComposeViewController。 重复使用同一个是不可靠的。
有一个全局例程释放,然后重新初始化单身MFMailComposeViewController
。 在完成邮件编辑器后,每次调用该全局例程。
做任何单身人士。 不要忘记,你的应用程序委托当然是一个单身人士,所以在那里做…
@property (nonatomic, strong) MFMailComposeViewController *globalMailComposer; -(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ........ // part 3, our own setup [self cycleTheGlobalMailComposer]; // needed due to the worst programming in the history of Apple ......... }
和…
-(void)cycleTheGlobalMailComposer { // cycling GlobalMailComposer due to idiotic iOS issue self.globalMailComposer = nil; self.globalMailComposer = [[MFMailComposeViewController alloc] init]; }
然后使用邮件,这样的事情…
-(void)helpEmail { // APP.globalMailComposer IS READY TO USE from app launch. // recycle it AFTER OUR USE. if ( [MFMailComposeViewController canSendMail] ) { [APP.globalMailComposer setToRecipients: [NSArray arrayWithObjects: emailAddressNSString, nil] ]; [APP.globalMailComposer setSubject:subject]; [APP.globalMailComposer setMessageBody:msg isHTML:NO]; APP.globalMailComposer.mailComposeDelegate = self; [self presentViewController:APP.globalMailComposer animated:YES completion:nil]; } else { [UIAlertView ok:@"Unable to mail. No email on this device?"]; [APP cycleTheGlobalMailComposer]; } } -(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error { [controller dismissViewControllerAnimated:YES completion:^ { [APP cycleTheGlobalMailComposer]; } ]; }
{nb,每个迈克尔Salamone固定错字下面}
在前缀文件中有以下macros为了方便
#define APP ((AppDelegate *)[[UIApplication sharedApplication] delegate])
此外,这是一个“小问题”,可能花费你几天: https : //stackoverflow.com/a/17120065/294884
只是2016年FTR这里是基本的SWIFT代码发送电子邮件在APP中,
class YourClass:UIViewController, MFMailComposeViewControllerDelegate { func clickedMetrieArrow() { print("click arrow! v1") let e = MFMailComposeViewController() e.mailComposeDelegate = self e.setToRecipients( ["help@smhk.com"] ) e.setSubject("Blah subject") e.setMessageBody("Blah text", isHTML: false) presentViewController(e, animated: true, completion: nil) } func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) { dismissViewControllerAnimated(true, completion: nil) }
然而! 注意!
这些天来发送电子邮件“在应用程序”是蹩脚的。
简单地切入电子邮件客户端,今天好多了。
添加到plist …
<key>LSApplicationQueriesSchemes</key> <array> <string>instagram</string> </array>
然后代码就像
func pointlessMarketingEmailForClient() { let subject = "Some subject" let body = "Plenty of <i>email</i> body." let coded = "mailto:blah@blah.com?subject=\(subject)&body=\(body)".stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet()) if let emailURL:NSURL = NSURL(string: coded!) { if UIApplication.sharedApplication().canOpenURL(emailURL) { UIApplication.sharedApplication().openURL(emailURL) } else { print("fail A") } } else { print("fail B") } }
现在,这比尝试从应用内部发送电子邮件好得多。
再次记住,iOS模拟器根本没有电子邮件客户端(也不能在应用程序中使用作曲者发送电子邮件)。 您必须在设备上进行testing。
它与Swift无关。 邮件composer php的问题似乎永远存在。 这件事是非常挑剔的,从超时失败到发送委托消息,即使取消。
每个人使用的解决方法是创build一个全球邮件编辑器(例如在一个单身人士),每当需要它时,重新初始化它。 这可以确保邮件编辑器始终在操作系统需要的时候,而且当你想要重用邮件时,它也没有任何垃圾。
因此,创build一个强大的(尽可能全局的)variables来保存邮件编辑器,并在每次使用时重置它。
- XCode 6模拟器在pipe理Mailcomposer和其他东西方面有问题。
- 尝试使用真实设备testing代码。 可能会起作用。
- 从actionSheetbutton运行MailComposer时,我也遇到了问题,也是真正的考验。 与IOS 7工作正常,在IOS 8相同的代码不起作用。 对我来说,苹果公司必须对Xcode 6进行净化。(Objective-C和Swift一起使用的模拟器件太多了…)
不确定上述解决scheme中提出的回收是否有必要。 但是你确实需要使用适当的参数。
委托收到一个MFMailComposeViewController* parameter
。 当解散控制器时,你需要使用它而不是self
。 即
代表收到(MFMailComposeViewController *) controller
。 而当你closuresMFMailComposeViewController controller
时,你需要使用它来代替self
。 毕竟这是你想要解雇的。
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error { [controller dismissViewControllerAnimated:YES completion:^ { [APP cycleTheGlobalMailComposer]; } ]; }
为邮件编辑器创build一个属性并在视图中实例化它,而不是在需要邮件编辑器时调用它。
@property (strong, nonatomic) MFMailComposeViewController *mailController; self.mailController = [[MFMailComposeViewController alloc] init]; [self presentViewController:self.mailController animated:YES completion:^{}];
嘿,这是在2天前发布的iOS 8.3解决。
Swift中处理邮件的简单帮助类。 基于乔·布洛的回答。
import UIKit import MessageUI public class EmailManager : NSObject, MFMailComposeViewControllerDelegate { var mailComposeViewController: MFMailComposeViewController? public override init() { mailComposeViewController = MFMailComposeViewController() } private func cycleMailComposer() { mailComposeViewController = nil mailComposeViewController = MFMailComposeViewController() } public func sendMailTo(emailList:[String], subject:String, body:String, fromViewController:UIViewController) { if MFMailComposeViewController.canSendMail() { mailComposeViewController!.setSubject(subject) mailComposeViewController!.setMessageBody(body, isHTML: false) mailComposeViewController!.setToRecipients(emailList) mailComposeViewController?.mailComposeDelegate = self fromViewController.presentViewController(mailComposeViewController!, animated: true, completion: nil) } else { print("Could not open email app") } } public func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) { controller.dismissViewControllerAnimated(true) { () -> Void in self.cycleMailComposer() } } }
作为实例variables放在AppDelegate类中,并在需要时调用。