2012-09-27 8 views
31

Estoy usando la nueva clase UIActivityViewController en iOS6 para proporcionar al usuario varias opciones de uso compartido. Puede pasarle una serie de parámetros, como texto, enlaces e imágenes, y hace el resto.¿Cómo configuro los destinatarios para UIActivityViewController en iOS 6?

¿Cómo se definen los destinatarios? Por ejemplo, compartir por correo o SMS debería poder aceptar destinatarios, pero no puedo encontrar la manera de invocar este comportamiento.

No quiero tener que tener que usar MFMessageComposeViewController y UIActivityViewController por separado, ya que eso acaba con el propósito del controlador compartido.

¿Alguna sugerencia?

UIActivityViewController Class Reference

Editar: Esto ya se ha presentado Apple y posteriormente se fusionó con un informe de error duplicado.

Bug report on OpenRadar

Respuesta

3

Todo el crédito aquí va a Emanuelle, ya que se le ocurrió la mayor parte del código.

Aunque pensé que publicaría una versión modificada de su código que ayuda a configurar el destinatario.

que utiliza una categoría en 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 
+4

Solución bastante horrible – Andy

+0

¿Puede compartir el ejemplo completo? – Rajeev

+0

¿Esto ha funcionado para cualquier persona? Si es así, ¿puedes compartir el código completo? – mKane

1

Usted debe ser capaz de incluir los destinatarios con un objeto NSURL con el mailto: Esquema (o SMS: para mensajes de texto).

A partir de la referencia de clase UIActivity:

UIActivityTypeMail Los puestos objeto el contenido proporcionado a un nuevo mensaje de correo electrónico . Al utilizar este servicio, puede proporcionar NSString y UIImage objetos y objetos NSURL apuntando a archivos locales como datos para los elementos de actividad. También puede especificar objetos NSURL cuyo contenido utilice el esquema mailto.

Por lo tanto, algo como esto debería funcionar:

NSString *text = @"My mail text"; 
NSURL *recipients = [NSURL URLWithString:@"mailto:[email protected]"]; 

NSArray *activityItems = @[text, recipients]; 

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

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

Gracias por la sugerencia. Lo intenté y el contenido de la URL de mailto se agregó al cuerpo del correo electrónico. Entonces parece que esa no es la solución a pesar de la documentación. – MattCheetham

+0

Hmm eso es extraño. ¿Podría intentar eliminar el texto de la matriz activityItems y simplemente pasar el NSURL con el mailto? – henning77

+0

Puede extender el correo para incluir el cuerpo, p. con mailto: [email protected]? cc=som[email protected]&subject=This%20is%20the%20subject&body=This%20is%20the%20body – henning77

8

Si bien parece que en la actualidad el mailto: solución para el establecimiento de asunto del correo electrónico y el cuerpo no está trabajando, esto sería en todo caso no será adecuado si desea establecer el cuerpo del correo electrónico para que contenga HTML y aún hacer uso del icono de correo electrónico del sistema de Apple a través de UIActivityViewController.

Eso es exactamente lo que queríamos hacer: usar el ícono del sistema, pero hacer que el correo electrónico contenga un cuerpo HTML y un asunto personalizado.

Nuestra solución fue algo así como un truco, pero funciona bien, al menos por el momento. Implica el uso de MFMailComposeViewController, pero todavía le permite usar el icono de correo del sistema con UIActivityViewController.

Paso 1: Crear una clase de contenedor conforme a la UIActivityItemSource así:

@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; 
    } 

Paso 2: Subclase UIActivityViewController y convertirlo en un MFMailComposeViewControllerDelegate así:

@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; 
    } 

NOTA: cuando se están llamando al super initWithActivityItems, estás ajustando el objeto que compartirás en tu ActivityItemSource personalizado

Paso 3: inicie su propio MFMailComposeViewController en lugar del sistema uno cuando un usuario toque el icono de Correo.

Se podría hacer esto en el método activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType en la clase 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]; 

    } 

En el mailComposeControllerWithObject método que se crea una instancia de la clase MFMailComposeViewController y configurarlo para que contenga todos los datos que desea. Tenga en cuenta también que establecería el activityViewController como el delegado de la vista de composición.

La razón por la que esto funciona es que cuando se muestra un modal de redacción, impide que se muestren otros modales, es decir, que muestre sus propios bloques de vista de composición, no se muestre la vista de composición del sistema. Definitivamente un truco, pero hace el trabajo bien.

Espero que esto ayude.

+1

¿Puede decirme cómo ' MFMailComposeViewController * mailController = [ShareViewController mailComposeControllerWithObject: self.object withDelegate: activityViewController]; 'funciona? Cuando intento 'MFMailComposeViewController * mailController = [[MFMailComposeViewController alloc] init]; mailController.delegate = activityViewController; 'Tengo' intento de presentar on que está esperando una presentación retrasada de para completar el error – zeroos

+0

Hola zeroos, hemos abandonado este enfoque hace un tiempo, por lo que es posible que Apple haya cerrado esta laguna mientras tanto. ¿A qué versión de iOS estás creando? Puede intentar construir en el simulador con un sistema operativo anterior, p. 5.1, solo para ver si está relacionado con el sistema operativo. – ilyashev

+0

Si es necesario, también puede intentar mostrar el mailComposeView desde un controlador de vista diferente. Presumiblemente, eso aún debería evitar que aparezca el correo estándarComposeView. – ilyashev

10

acabo de encontrar una solución a este problema (en mi caso establecer el asunto del correo electrónico): como internamente la UIActivityViewController llamará en algún momento el setMessageBody: isHTML: método de la clase MFMailComposeViewController, acaba de intercepción que call and inside realiza una llamada al método setSubject: Gracias a "método swizzling" técnica, que parece:

#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 

Llame a la siguiente línea de código antes de usar UIActivityViewController:

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

A continuación, pasar a la UIActivityViewController una costumbre UIActivityItemProvider que por UIActivityTypeMail devuelve un HTML NSString como:

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

el asunto del correo electrónico se extrae del título HTML (utilizar texto sin formato para esa parte, sin entidades o etiquetas html).

Usando ese método, le dejo elaborar una forma elegante de configurar el destinatario para el correo.

24

Para añadir sujeto al correo electrónico usando UIActivityViewController en iOS6, esta es la mejor solución que cualquiera puede utilizar .. Todo lo que tiene que hacer es llamar a los siguientes UIActivityViewController durante la inicialización.

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

Y su UIActivityViewController se completa con un asunto.

+0

Funcionó como un encanto y muy simple. ¡Gracias! – Jon

+1

Esto parece funcionar, pero ¿está documentado? –

+1

¡Gran solución con KVO! – manderson

1

No estoy seguro acerca de los destinatarios, pero parece que en iOS 7 y posterior puede establecer el asunto de un correo electrónico conforme al protocolo UIActivityItemSource y aplicando el método activityViewController:subjectForActivityType:.

+3

Sí, esto funciona para el sujeto, sin problemas. Pero todavía no hay una forma no swizzly para configurar el destinatario. – matt

Cuestiones relacionadas