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

33

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

如何定义收件人? 例如,通过电子邮件或短信进行共享应该能够接受收件人,但我无法弄清楚如何调用此行为。

我不想分别使用MFMessageComposeViewControllerUIActivityViewController,因为那样就失去了共享控制器的目的。

有任何建议吗?

UIActivityViewController 类参考

编辑:这已经被提交给苹果并随后与重复的错误报告合并。

OpenRadar上的错误报告

6个回答

24

对于在iOS6上使用UIActivityViewController为电子邮件添加主题,这是任何人都可以使用的最佳解决方案。您只需在初始化UIActivityViewController时调用以下内容即可。

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

您的UIActivityViewController已填充主题。


运行得非常好,而且非常简单。谢谢! - Jon
1
使用KVO的好解决方案! - manderson
14
收件人怎么样?我们可以像这样添加吗? - Min Soe
1
这是仅在邮件中添加主题的操作。 - Abhishek Thapliyal

10

我刚想到了解决这个问题的方法(在我的情况下是设置电子邮件主题):因为UIActivityViewController内部会在某个时候调用MFMailComposeViewController类的setMessageBody:isHTML:方法,所以只需拦截该调用并在内部调用setSubject:方法。利用"方法交换"技术,代码如下:

我刚想到了解决这个问题的方法(在我的情况下是设置电子邮件主题):因为UIActivityViewController内部会在某个时候调用MFMailComposeViewController类的setMessageBody:isHTML:方法,所以只需拦截该调用并在内部调用setSubject:方法。利用"方法交换"技术,代码如下:

#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字符串,如下所示:

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

邮件主题从HTML标题中提取(使用纯文本,不含html实体或标签)。

使用该方法,我让您详细阐述了一种设置邮件收件人的优雅方式。


8

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

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

我们的解决方案有点像黑客方式,但它目前运行良好。 它确实涉及使用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方法,但它能胜任工作。
希望这有所帮助。

1
你能告诉我 MFMailComposeViewController *mailController = [ShareViewController mailComposeControllerWithObject: self.object withDelegate: activityViewController]; 是怎么工作的吗?当我尝试使用 MFMailComposeViewController *mailController = [[MFMailComposeViewController alloc] init]; mailController.delegate = activityViewController; 时,我得到了一个错误提示:Attempt to present <MFMailComposeViewController: 0x1edbc610> on <ActivityViewController: 0x20037560> which is waiting for a delayed presention of <MFMailComposeViewController: 0x200368b0> to complete - zeroos
我正在使用iOS 6.1,在模拟的iOS 6.0中遇到了同样的问题。UIActiveViewController在iOS 5.1中不可用,因此我正在寻找其他解决方案。 - zeroos
没错,UIActivityViewController是在iOS6中推出的。在我们的情况下,我们最终创建了一个自定义等效的活动视图控制器,这使我们完全控制图标的外观以及在我们显示的社交视图中显示的内容。 - ilyashev
@ilyashev,我该如何在具有共享邮件功能的控制器中使用此代码?请告诉我。 - Nikunj Jadav
@ilyashev 在 ActivityViewController 中点击邮件图标时,itemForActivityType 方法未被调用。请评论。 - Nikunj Jadav
显示剩余5条评论

3

所有的功劳都归给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

你能分享完整的例子吗? - Rajeev
这个对任何人都有效吗?如果是的话,你能分享完整的代码吗? - mKane

1

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

从UIActivity类参考文档中:

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

因此,像这样的东西应该可以工作:

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];

5
谢谢您的建议。我已经尝试过了,发现 mailto URL 的内容只是被添加到电子邮件正文中。因此,尽管有相关文档的支持,看起来那并不是解决方案。 - MattCheetham
刚刚也尝试了一下,不知道是否必须在其他内容之前加上mailto,但它仍然放到了正文中。令人沮丧。编辑:我会尝试一下第二部分的建议。 - MattCheetham
不行。将其作为字符串或NSURL传递仍会将整个内容打印到电子邮件正文中。开始觉得这是不可能的。 - MattCheetham
2
你应该能够做到,但实际上却不能。在iOS 7的文档中,关于mailto: URL的声明被撤回了。它从未像广告中所说的那样工作。 - matt
1
请查看文档链接:https://developer.apple.com/reference/uikit/uiactivitytypemail?language=objc,您会发现任何使用mailto:设置收件人的参考都已被删除。 - bengoesboom
显示剩余4条评论

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

3
没问题,这对主题有效。但是仍然没有一种不混乱的方式来设置接收者。 - matt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接