Como faço para definir os destinatários do UIActivityViewController no iOS 6?

Estou usando a nova class UIActivityViewController no iOS6 para fornecer ao usuário várias opções de compartilhamento. Você pode passar uma matriz de parâmetros para ele, como texto, links e imagens, e faz o resto.

Como eu defino os destinatários? Por exemplo, o compartilhamento via correio ou SMS deve ser capaz de aceitar destinatários, mas não consigo descobrir como invocar esse comportamento.

Eu não quero ter que usar MFMessageComposeViewController e UIActivityViewController separadamente, pois isso acaba com a finalidade do controlador de compartilhamento.

Alguma sugestão?

Referência de class UIActivityViewController

Edit: Este foi submetido a Apple e, posteriormente, se fundiu com um relatório de bug duplicado.

Relatório de Bug no OpenRadar

Todo o crédito aqui vai para Emanuelle, já que ele inventou a maior parte do código.

Embora eu pensasse em postar uma versão modificada de seu código que ajuda a definir o destinatário.

Eu usei uma categoria no MFMailComposeViewController

 #import "MFMailComposeViewController+Recipient.h" #import  @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:@".*" options:NSRegularExpressionSearch|NSCaseInsensitiveSearch]; if (range.location != NSNotFound) { NSScanner *scanner = [NSScanner scannerWithString:body]; [scanner setScanLocation:range.location+14]; NSString *recipientsString = [NSString string]; if ([scanner scanUpToString:@"" intoString:&recipientsString] == YES) { NSArray * recipients = [recipientsString componentsSeparatedByString:@";"]; [self setToRecipients:recipients]; } body = [body stringByReplacingCharactersInRange:range withString:@""]; } } [self setMessageBodySwizzled:body isHTML:isHTML]; } @end 

Para adicionar assunto ao email usando o UIActivityViewController no iOS6, esta é a melhor solução que qualquer um pode usar. Tudo o que você precisa fazer é chamar o seguinte ao inicializar o UIActivityViewController.

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

E seu UIActivityViewController é preenchido com um assunto.

Acabei de chegar a uma solução para este problema (no meu caso definir o assunto do email): como internamente o UIActivityViewController irá chamar em algum momento o método setMessageBody: isHTML: da class MFMailComposeViewController, apenas interceptar essa chamada e fazer dentro de um chame para o método setSubject :. Graças à técnica “método swizzling”, parece que:

 #import  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:@".*" options:NSRegularExpressionSearch|NSCaseInsensitiveSearch]; if (range.location != NSNotFound) { NSScanner *scanner = [NSScanner scannerWithString:body]; [scanner setScanLocation:range.location+7]; NSString *subject = [NSString string]; if ([scanner scanUpToString:@"" intoString:&subject] == YES) { [self setSubject:subject]; } } } [self setMessageBodySwizzled:body isHTML:isHTML]; } @end 

Chame a seguinte linha de código antes de usar o UIActivityViewController:

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

Em seguida, passe para o UIActivityViewController um UIActivityItemProvider personalizado que para UIActivityTypeMail retorne uma NSString HTML como:

  Subject of the mail  Body of the mail  

O assunto do email é extraído do título HTML (use texto simples para essa parte, sem entidades ou tags html).

Usando esse método, deixo você elaborar uma maneira elegante de definir o destinatário para o email.

Embora pareça que, no momento, a solução mailto: para definir o assunto e o corpo do email não está funcionando, isso não seria adequado se você quisesse configurar o corpo do email para conter HTML e ainda usar o ícone de email do sistema da Apple via UIActivityViewController.

Era exatamente o que queríamos fazer: usar o ícone do sistema, mas o email deve conter um corpo de HTML e um assunto personalizado.

Nossa solução foi uma espécie de hack, mas funciona bem, pelo menos por enquanto. Ele envolve o uso de MFMailComposeViewController, mas ainda permite usar o ícone de correio do sistema com o UIActivityViewController.

Etapa 1: crie uma class de wrapper de acordo com o UIActivityItemSource da seguinte forma:

  @interface ActivityItemSource : NSObject  @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; } 

Etapa 2: Subclass UIActivityViewController e torne-o um MFMailComposeViewControllerDelegate da seguinte forma:

  @interface ActivityViewController : UIActivityViewController  @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; } 

NOTA: quando você está chamando super initWithActivityItems você está empacotando o object que será compartilhado em seu ActivityItemSource customizado.

Etapa 3: inicie seu próprio MFMailComposeViewController em vez do sistema um quando um usuário tocar no ícone Email.

Você faria isso no activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType na class ActivityItemSource:

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

No método mailComposeControllerWithObject , você instancia uma instância da class MFMailComposeViewController e a configura para conter os dados desejados. Observe também que você configuraria o activityViewController como o representante da visão de composição.

A razão pela qual isso funciona é que quando um modal de composição é exibido, ele impede que outros modais sejam exibidos, ou seja, você exibindo sua própria visão de composição bloqueia a visualização de composição do sistema. Definitivamente, um hack, mas faz o trabalho.

Espero que isto ajude.

Você deve ser capaz de include os destinatários usando um object NSUrl com o esquema mailto: (ou sms: para mensagens de texto).

A partir da referência da class UIActivity:

UIActivityTypeMail O object envia o conteúdo fornecido para uma nova mensagem de email. Ao usar esse serviço, você pode fornecer objects NSString e UIImage e objects NSURL apontando para arquivos locais como dados para os itens de atividade. Você também pode especificar objects NSURL cujo conteúdo usa o esquema mailto.

Portanto, algo como isto deve funcionar:

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

Não tenho certeza sobre destinatários, mas parece que, no iOS 7 e posterior, você pode definir o assunto de um email em conformidade com o protocolo UIActivityItemSource e implementar o método activityViewController:subjectForActivityType: