可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm using the new UIActivityViewController
class in iOS6 to provide the user with various sharing options. You can pass an array of parameters to it such as text, links and images and it does the rest.
How do I define recipients? For example sharing via mail or SMS should be able to accept recipients but I can't figure out how to invoke this behaviour.
I don't want to have to have to use MFMessageComposeViewController
and UIActivityViewController
separately as that just defeats the purpose of the share controller.
Any suggestions?
UIActivityViewController Class Reference
Edit: This has now been submitted Apple and subsequently merged with a duplicate bug report.
Bug report on OpenRadar
回答1:
All credit here goes to Emanuelle, since he came up with most of the code.
Though I thought I would post a modified version of his code that helps set the to recipient.
I used a Category on 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
回答2:
For adding subject to the email using UIActivityViewController on iOS6, this is the best solution that anyone can use.. All you have to do is call the following while initializing UIActivityViewController.
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:applicationActivities];
[activityViewController setValue:@"My Subject Text" forKey:@"subject"];
And your UIActivityViewController is populated with a subject.
回答3:
I just come up with a solution to this problem (in my case set the subject of the email):
as internally the UIActivityViewController will call at some point the setMessageBody:isHTML: method of the MFMailComposeViewController class, just intercept that call and inside make a call to the setSubject: method. Thanks to "method swizzling" technic, it looks like:
#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
Call the following line of code before using UIActivityViewController:
MethodSwizzle([MFMailComposeViewController class], @selector(setMessageBody:isHTML:), @selector(setMessageBodySwizzled:isHTML:));
Then pass to the UIActivityViewController a custom UIActivityItemProvider that for UIActivityTypeMail returns a HTML NSString like:
<html><head>
<title>Subject of the mail</title>
</head><body>
Body of the <b>mail</b>
</body></html>
The subject of the email is extracted from the HTML title (use plain text for that part, no html entities or tags).
Using that method, I let you elaborate an elegant way to set the recipient for the mail.
回答4:
While it does appear that at present the mailto: solution for setting email subject and body isn't working, this would in any case not be adequate if you wanted to set the email body to contain HTML and still make use of Apple's system email icon via UIActivityViewController.
That was exactly what we wanted to do: use the system icon, but have the email contain an HTML body and a custom subject.
Our solution was something of a hack, but it works well, at least for the moment. It does involve using MFMailComposeViewController, but it still lets you use the system mail icon with UIActivityViewController.
Step 1: Create a wrapper class conforming to the UIActivityItemSource like so:
@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;
}
Step 2: Subclass UIActivityViewController and make it into a MFMailComposeViewControllerDelegate like so:
@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;
}
NOTE: when you are calling super initWithActivityItems
you are wrapping the object you will be sharing in your custom ActivityItemSource
Step 3: Launch your own MFMailComposeViewController instead of the system one when a user taps on the Mail icon.
You would do this in the activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType
method in the ActivityItemSource class:
- (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];
}
In the mailComposeControllerWithObject
method you instantiate an instance of the MFMailComposeViewController class and set it up to contain whatever data you want. Note also that you would set the activityViewController
as the compose view's delegate.
The reason this works is that when a compose modal is displayed, it prevents other modals from being displayed, i.e. you displaying your own compose view blocks the system compose view from being shown. Definitely a hack, but it gets the job done.
Hope this helps.
回答5:
You should be able to include the recipients using an NSUrl object with the mailto: scheme (or sms: for text messages).
From the UIActivity class reference:
UIActivityTypeMail
The object posts the provided content to a new
email message. When using this service, you can provide NSString and
UIImage objects and NSURL objects pointing to local files as data for
the activity items. You may also specify NSURL objects whose contents
use the mailto scheme.
Therefore, something like this should work:
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];
回答6:
I'm not sure about recipients, but it seems as though in iOS 7 and later you can set the subject of an email by conforming to the UIActivityItemSource
protocol and implementing the method activityViewController:subjectForActivityType:
.