如何在 iOS 6 中为 UIActivityViewController 设置收件人?

Posted

技术标签:

【中文标题】如何在 iOS 6 中为 UIActivityViewController 设置收件人?【英文标题】:How do I set recipients for UIActivityViewController in iOS 6? 【发布时间】:2012-09-27 14:04:35 【问题描述】:

我在 ios6 中使用新的UIActivityViewController 类为用户提供各种共享选项。您可以将一组参数传递给它,例如文本、链接和图像,其余的由它完成。

如何定义收件人?例如,通过邮件或 SMS 共享应该能够接受收件人,但我不知道如何调用此行为。

我不想必须分别使用MFMessageComposeViewControllerUIActivityViewController,因为这违背了共享控制器的目的。

有什么建议吗?

UIActivityViewController Class Reference

编辑:这现已提交给 Apple,随后与重复的错误报告合并。

Bug report on OpenRadar

【问题讨论】:

【参考方案1】:

要在 iOS6 上使用 UIActivityViewController 将 主题 添加到电子邮件中,这是任何人都可以使用的最佳解决方案。您所要做的就是在初始化 UIActivityViewController 时调用以下命令。

UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:applicationActivities];
[activityViewController setValue:@"My Subject Text" forKey:@"subject"];

你的 UIActivityViewController 填充了一个主题。

【讨论】:

工作起来就像一个魅力,非常简单。谢谢! 使用 KVO 的绝佳解决方案! 收件人呢?我们可以这样添加吗? 这只是在邮件中添加主题【参考方案2】:

我只是想出了一个解决这个问题的方法(在我的例子中,设置电子邮件的主题): 在内部,UIActivityViewController 将在某个时候调用 MFMailComposeViewController 类的 setMessageBody:ishtml: 方法,只需拦截该调用并在内部调用 setSubject: 方法。多亏了“method swizzling”技术,它看起来像:

#import <objc/message.h>

static void MethodSwizzle(Class c, SEL origSEL, SEL overrideSEL)

    Method origMethod = class_getInstanceMethod(c, origSEL);
    Method overrideMethod = class_getInstanceMethod(c, overrideSEL);

    if (class_addMethod(c, origSEL, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod))) 
        class_replaceMethod(c, overrideSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
     else 
        method_exchangeImplementations(origMethod, overrideMethod);
    


@implementation MFMailComposeViewController (force_subject)

- (void)setMessageBodySwizzled:(NSString*)body isHTML:(BOOL)isHTML

    if (isHTML == YES) 
        NSRange range = [body rangeOfString:@"<title>.*</title>" options:NSRegularExpressionSearch|NSCaseInsensitiveSearch];
        if (range.location != NSNotFound) 
            NSScanner *scanner = [NSScanner scannerWithString:body];
            [scanner setScanLocation:range.location+7];
            NSString *subject = [NSString string];
            if ([scanner scanUpToString:@"</title>" intoString:&subject] == YES) 
                [self setSubject:subject];
            
        
    
    [self setMessageBodySwizzled:body isHTML:isHTML];


@end

在使用 UIActivityViewController 之前调用下面这行代码:

MethodSwizzle([MFMailComposeViewController class], @selector(setMessageBody:isHTML:), @selector(setMessageBodySwizzled:isHTML:));

然后将一个自定义 UIActivityItemProvider 传递给 UIActivityViewController,它会为 UIActivityTypeMail 返回一个 HTML NSString,例如:

<html><head>
<title>Subject of the mail</title>
</head><body>
Body of the <b>mail</b>
</body></html>

电子邮件的主题是从 HTML 标题中提取的(该部分使用纯文本,没有 html 实体或标签)。

使用该方法,我让您详细说明一种设置邮件收件人的优雅方法。

【讨论】:

【参考方案3】:

虽然目前看来 mailto: 设置电子邮件主题和正文的解决方案不起作用,但如果您想将电子邮件正文设置为包含 HTML 并仍然使用 Apple 的通过 UIActivityViewController 的系统电子邮件图标。

这正是我们想要做的:使用系统图标,但让电子邮件包含 HTML 正文和自定义主题。

我们的解决方案有点像 hack,但效果很好,至少目前是这样。它确实涉及使用 MFMailComposeViewController,但它仍然允许您将系统邮件图标与 UIActivityViewController 一起使用。

第 1 步:创建一个符合 UIActivityItemSource 的包装类,如下所示:

    @interface ActivityItemSource : NSObject <UIActivityItemSource>
    @property (nonatomic, strong) id object;
    - (id) initWithObject:(id) objectToUse;
    @end

    @implementation ActivityItemSource

   - (id) initWithObject:(id) objectToUse
    
        self = [super init];
        if (self) 
            self.object = objectToUse;
        
        return self;
    


    - (id)activityViewController:(UIActivityViewController *)activityViewController                 itemForActivityType:(NSString *)activityType
    
    return self.object;
    

    - (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController
    

        return self.object;
    

第 2 步:继承 UIActivityViewController 并将其变成 MFMailComposeViewControllerDelegate,如下所示:

    @interface ActivityViewController : UIActivityViewController         <MFMailComposeViewControllerDelegate>

    @property (nonatomic, strong) id object;

    - (id) initWithObject:(id) objectToUse;
    @end


    @implementation ActivityViewController

    - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
    

        switch (result)
        
            case MFMailComposeResultSent:
            case MFMailComposeResultSaved:
                //successfully composed an email
                break;
            case MFMailComposeResultCancelled:
                break;
            case MFMailComposeResultFailed:
                break;
        

    //dismiss the compose view and then the action view
        [self dismissViewControllerAnimated:YES completion:^() 
            [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];        
        ];

    

    - (id) initWithObject:(id) objectToUse
    

        self = [super initWithActivityItems:[NSArray arrayWithObjects:[[ActivityItemSource alloc] initWithObject:objectToUse], nil] applicationActivities:nil];

        if (self) 
            self.excludedActivityTypes = [NSArray arrayWithObjects: UIActivityTypePostToWeibo, UIActivityTypePrint, UIActivityTypeCopyToPasteboard, UIActivityTypeAssignToContact, UIActivityTypeSaveToCameraRoll, nil];

            self.object = objectToUse;
        
        return self;
    

注意:当您调用 super initWithActivityItems 时,您将在自定义 ActivityItemSource 中包装您将共享的对象

第 3 步:当用户点击邮件图标时,启动您自己的 MFMailComposeViewController 而不是系统控制器。

您可以在 ActivityItemSource 类的 activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType 方法中执行此操作:

    - (id)activityViewController:(UIActivityViewController *)activityViewController                 itemForActivityType:(NSString *)activityType
    
        if([activityType isEqualToString:UIActivityTypeMail]) 
                //TODO: fix; this is a hack; but we have to wait till apple fixes the         inability to set subject and html body of email when using UIActivityViewController
                [self setEmailContent:activityViewController];
                return nil;
            
         return self.object;
    


    - (void) setEmailContent:(UIActivityViewController *)activityViewController 
    

       MFMailComposeViewController *mailController = [ShareViewController mailComposeControllerWithObject: self.object withDelegate: activityViewController];

        [activityViewController presentViewController:mailController animated:YES completion:nil];

    

mailComposeControllerWithObject 方法中,您实例化 MFMailComposeViewController 类的一个实例,并将其设置为包含您想要的任何数据。另请注意,您会将activityViewController 设置为撰写视图的委托。

这样做的原因是当显示组合模式时,它会阻止显示其他模式,即显示自己的组合视图会阻止系统组合视图的显示。绝对是一个 hack,但它可以完成工作。

希望这会有所帮助。

【讨论】:

您能告诉我MFMailComposeViewController *mailController = [ShareViewController mailComposeControllerWithObject: self.object withDelegate: activityViewController]; 的工作原理吗?当我尝试MFMailComposeViewController *mailController = [[MFMailComposeViewController alloc] init]; mailController.delegate = activityViewController; 时,我得到Attempt to present &lt;MFMailComposeViewController: 0x1edbc610&gt; on &lt;ActivityViewController: 0x20037560&gt; which is waiting for a delayed presention of &lt;MFMailComposeViewController: 0x200368b0&gt; to complete 错误 嗨 zeroos,我们不久前已经放弃了这种方法,所以苹果可能在此期间已经关闭了这个漏洞。您正在构建哪个 iOS 版本?您可以尝试使用较早的操作系统构建模拟器,例如5.1,只是看看它是否与操作系统相关。 如果必须,您也可以尝试从不同的视图控制器显示 mailComposeView。据推测,这仍然会阻止标准的 mailComposeView 出现。 我使用的是 iOS 6.1,在模拟的 iOS 6.0 中我遇到了同样的问题。 UIActiveViewController 在 iOS 5.1 中不可用,所以我正在寻找其他解决方案。 对,UIActivityViewController 是随 iOS6 进来的。在我们的案例中,我们最终创建了一个与活动视图控制器等效的自定义,它使我们能够完全控制图标的显示方式以及我们显示的社交视图中显示的内容。【参考方案4】:

这里的所有功劳都归功于 Emanuelle,因为他提出了大部分代码。

虽然我认为我会发布他的代码的修改版本,以帮助设置收件人。

我在 MFMailComposeViewController 上使用了一个类别

#import "MFMailComposeViewController+Recipient.h"
#import <objc/message.h>

@implementation MFMailComposeViewController (Recipient)

+ (void)load 
    MethodSwizzle(self, @selector(setMessageBody:isHTML:), @selector(setMessageBodySwizzled:isHTML:));




static void MethodSwizzle(Class c, SEL origSEL, SEL overrideSEL)

    Method origMethod = class_getInstanceMethod(c, origSEL);
    Method overrideMethod = class_getInstanceMethod(c, overrideSEL);

    if (class_addMethod(c, origSEL, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod))) 
        class_replaceMethod(c, overrideSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
     else 
        method_exchangeImplementations(origMethod, overrideMethod);
    


- (void)setMessageBodySwizzled:(NSString*)body isHTML:(BOOL)isHTML

    if (isHTML == YES) 
        NSRange range = [body rangeOfString:@"<torecipients>.*</torecipients>" options:NSRegularExpressionSearch|NSCaseInsensitiveSearch];
        if (range.location != NSNotFound) 
            NSScanner *scanner = [NSScanner scannerWithString:body];
            [scanner setScanLocation:range.location+14];
            NSString *recipientsString = [NSString string];
            if ([scanner scanUpToString:@"</torecipients>" intoString:&recipientsString] == YES) 
                NSArray * recipients = [recipientsString componentsSeparatedByString:@";"];
                [self setToRecipients:recipients];
            
            body = [body stringByReplacingCharactersInRange:range withString:@""];
        
    
    [self setMessageBodySwizzled:body isHTML:isHTML];


@end

【讨论】:

你能分享完整的例子吗? 这对任何人都有效吗?如果可以,您可以分享完整的代码吗?【参考方案5】:

您应该能够使用带有 mailto: 方案(或 sms: 用于文本消息)的 NSUrl 对象来包含收件人。

来自 UIActivity 类参考:

UIActivityTypeMail 该对象将提供的内容发布到一个新的 电子邮件。使用此服务时,您可以提供 NSString 和 UIImage 对象和 NSURL 对象指向本地文件作为数据 活动项目。你也可以指定 NSURL 对象的内容 使用 mailto 方案。

因此,这样的事情应该可以工作:

NSString *text = @"My mail text";
NSURL *recipients = [NSURL URLWithString:@"mailto:foo@bar.com"];

NSArray *activityItems = @[text, recipients];

UIActivityViewController *activityController =
                    [[UIActivityViewController alloc]
                    initWithActivityItems:activityItems
                    applicationActivities:nil];

[self presentViewController:activityController
                   animated:YES completion:nil];

【讨论】:

感谢您的建议。我已经尝试过了,mailto URL 的内容刚刚被添加到电子邮件的正文中。因此,尽管有文档,但这似乎不是解决方案。 嗯,这很奇怪。您可以尝试从 activityItems 数组中删除文本,然后将 NSURL 与 mailto 一起传递吗? 您可以扩展 mailto 以包含正文,例如与 mailto:someone@example.com?cc=someone_else@example.com&subject=This%20is%20the%20subject&body=This%20is%20the%20body 应该能够做到这一点,但你不能。在 iOS 7 文档中,关于mailto: URL 的声明已被撤销。它从来没有像宣传的那样起作用。 developer.apple.com/reference/uikit/… 文档链接:您会看到所有关于使用 mailto: 设置收件人的参考都已删除【参考方案6】:

我不确定收件人,但似乎在 iOS 7 及更高版本中,您可以通过符合 UIActivityItemSource 协议并实现方法 activityViewController:subjectForActivityType: 来设置电子邮件的主题。

【讨论】:

是的,这确实适用于主题,没有汗水。但是仍然没有设置收件人的非混乱方式。

以上是关于如何在 iOS 6 中为 UIActivityViewController 设置收件人?的主要内容,如果未能解决你的问题,请参考以下文章

如何在同一应用程序中为 iOS 6 和 iOS 7 版本设置默认启动图像

使用 iOS6 UIActivityViewController 附加对象

如何在 iOS 中为特定设备设置顶部空间限制?

如何在 Xcode 6 的 XIB Interface Builder 中为 Mac 应用程序分配按钮

在 iOS 6 中为我的应用程序登录 Facebook [关闭]

XCode 6.0.1 ios 7 自动布局