ios objective-c ios8 uiactivityviewcontroller

UIActivityViewController se bloquea en iPads con iOS 8



objective-c ios8 (19)

Actualmente estoy probando mi aplicación con Xcode 6 (Beta 6). UIActivityViewController funciona bien con dispositivos y simuladores de iPhone, pero se bloquea con simuladores y dispositivos de iPad (iOS 8) con los siguientes registros

Terminating app due to uncaught exception ''NSGenericException'', reason: ''UIPopoverPresentationController (<_UIAlertControllerActionSheetRegularPresentationController: 0x7fc7a874bd90>) should have a non-nil sourceView or barButtonItem set before the presentation occurs.

Estoy usando el siguiente código para iPhone y iPad tanto para iOS 7 como para iOS 8

NSData *myData = [NSData dataWithContentsOfFile:_filename]; NSArray *activityItems = [NSArray arrayWithObjects:myData, nil]; UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:nil applicationActivities:nil]; activityViewController.excludedActivityTypes = @[UIActivityTypeCopyToPasteboard]; [self presentViewController:activityViewController animated:YES completion:nil];

Estoy teniendo un bloqueo similar en una de mis otras aplicaciones también. ¿Puedes por favor guiarme? ¿Ha cambiado algo con UIActivityViewController en iOS 8? Lo comprobé pero no encontré nada sobre esto


Arreglo para Swift 2.0

if UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.Phone { self.presentViewController(activityVC, animated: true, completion: nil) } else { let popup: UIPopoverController = UIPopoverController(contentViewController: activityVC) popup.presentPopoverFromRect(CGRectMake(self.view.frame.size.width / 2, self.view.frame.size.height / 4, 0, 0), inView: self.view, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true) }


El mismo problema vino a mi proyecto, entonces encontré la solución de que para abrir UIActivityViewController en iPad tenemos que usar UIPopoverController

Aquí hay un código para usarlo tanto en iPhone como en iPad:

//to attach the image and text with sharing UIImage *image=[UIImage imageNamed:@"giraffe.png"]; NSString *str=@"Image form My app"; NSArray *postItems=@[str,image]; UIActivityViewController *controller = [[UIActivityViewController alloc] initWithActivityItems:postItems applicationActivities:nil]; //if iPhone if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { [self presentViewController:controller animated:YES completion:nil]; } //if iPad else { // Change Rect to position Popover UIPopoverController *popup = [[UIPopoverController alloc] initWithContentViewController:controller]; [popup presentPopoverFromRect:CGRectMake(self.view.frame.size.width/2, self.view.frame.size.height/4, 0, 0)inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; }

Para swift 4.2 / swift 5

func openShareDilog() { let text = "share text will goes here" // set up activity view controller let textToShare = [text] let activityViewController = UIActivityViewController(activityItems: textToShare, applicationActivities: nil) activityViewController.excludedActivityTypes = [.airDrop] if let popoverController = activityViewController.popoverPresentationController { popoverController.sourceRect = CGRect(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height / 2, width: 0, height: 0) popoverController.sourceView = self.view popoverController.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0) } self.present(activityViewController, animated: true, completion: nil) }


En Swift para arreglar esto para iPad, la mejor manera es hacer lo que encontré.

let things = ["Things to share"] let avc = UIActivityViewController(activityItems:things, applicationActivities:nil) avc.setValue("Subject title", forKey: "subject") avc.completionWithItemsHandler = { (s: String!, ok: Bool, items: [AnyObject]!, err:NSError!) -> Void in } self.presentViewController(avc, animated:true, completion:nil) if let pop = avc.popoverPresentationController { let v = sender as! UIView // sender would be the button view tapped, but could be any view pop.sourceView = v pop.sourceRect = v.bounds }


En iPad, el controlador de vista de actividad se mostrará como un popover usando el nuevo UIPopoverPresentationController , requiere que especifique un punto de anclaje para la presentación del popover usando una de las tres propiedades siguientes:

Para especificar el punto de anclaje, deberá obtener una referencia al UIPopoverPresentationController de UIActivityController y establecer una de las propiedades de la siguiente manera:

if ( [activityViewController respondsToSelector:@selector(popoverPresentationController)] ) { // iOS8 activityViewController.popoverPresentationController.sourceView = parentView; }


En swift 4 el siguiente código funciona en iphone y ipad. De acuerdo con la documentación

Es su responsabilidad presentar y descartar el controlador de vista utilizando los medios apropiados para el idioma del dispositivo dado. En iPad, debe presentar el controlador de vista en un popover. En otros dispositivos, debe presentarlo modalmente.

let activityViewController = UIActivityViewController(activityItems: activityitems, applicationActivities: nil) if UIDevice.current.userInterfaceIdiom == .pad { if activityViewController.responds(to: #selector(getter: UIViewController.popoverPresentationController)) { activityViewController.popoverPresentationController?.sourceView = self.view } } self.present(activityViewController, animated: true, completion: nil)


Encontré esta solución En primer lugar, su controlador de vista que presenta el popover debe implementar el protocolo <UIPopoverPresentationControllerDelegate> .

A continuación, deberá configurar el delegado de popoverPresentationController .

Agregue estas funciones:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Assuming you''ve hooked this all up in a Storyboard with a popover presentation style if ([segue.identifier isEqualToString:@"showPopover"]) { UINavigationController *destNav = segue.destinationViewController; PopoverContentsViewController *vc = destNav.viewControllers.firstObject; // This is the important part UIPopoverPresentationController *popPC = destNav.popoverPresentationController; popPC.delegate = self; } } - (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController: (UIPresentationController *)controller { return UIModalPresentationNone; }


Intenté el siguiente código y funciona:

primero coloque un elemento de botón de barra en su controlador de vista y luego cree un IBOutlet:

@property(weak,nonatomic)IBOutlet UIBarButtonItem *barButtonItem;

siguiente en el archivo .m: yourUIActivityViewController.popoverPresentationController.barButtonItem = self.barButtonItem;


Me encontré con este problema exacto recientemente (la pregunta original) en Swift 2.0, donde UIActivityViewController funcionó bien para iPhones, pero causó fallas al simular iPads.

Solo quiero agregar a este hilo de respuestas aquí que, al menos en Swift 2.0, no necesita una declaración if. Simplemente puede hacer que popoverPresentationController opcional.

Como comentario rápido, la respuesta aceptada parece estar diciendo que podría tener solo sourceView, solo sourceRect o simplemente barButtonItem, pero de acuerdo con la documentación de Apple para UIPopoverPresentationController , necesita uno de los siguientes:

  • barButtonItem
  • sourceView y sourceRect

El ejemplo particular en el que estaba trabajando es el siguiente, donde estoy creando una función que toma una UIView (para sourceView y sourceRect) y String (el único ActivityItem de UIActivityViewController).

func presentActivityViewController(sourceView: UIView, activityItem: String ) { let activityViewController = UIActivityViewController(activityItems: [activityItem], applicationActivities: []) activityViewController.popoverPresentationController?.sourceView = sourceView activityViewController.popoverPresentationController?.sourceRect = sourceView.bounds self.presentViewController(activityViewController, animated: true, completion: nil) }

Este código funciona en iPhone y iPad (e incluso en tvOS, creo): si el dispositivo no admite popoverPresentationController , las dos líneas de código que lo mencionan se ignoran esencialmente.

¡Qué bueno que todo lo que necesita hacer para que funcione para iPads es simplemente agregar dos líneas de código, o solo una si está usando un barButtonItem!


Para Swift 2.0. Descubrí que esto funciona si estás tratando de anclar el popover a un botón de compartir en iPad. Esto supone que ha creado una salida para el botón compartir en su barra de herramientas.

func share(sender: AnyObject) { let firstActivityItem = "test" let activityViewController = UIActivityViewController(activityItems: [firstActivityItem], applicationActivities: nil) if UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.Phone { self.presentViewController(activityViewController, animated: true, completion: nil) } else { if activityViewController.respondsToSelector("popoverPresentationController") { activityViewController.popoverPresentationController!.barButtonItem = sender as? UIBarButtonItem self.presentViewController(activityViewController, animated: true, completion: nil) } } }


Rápido:

let activityViewController = UIActivityViewController(activityItems: sharingItems, applicationActivities: nil) //if iPhone if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.Phone) { self.presentViewController(activityViewController, animated: true, completion: nil) } else { //if iPad // Change Rect to position Popover var popoverCntlr = UIPopoverController(contentViewController: activityViewController) popoverCntlr.presentPopoverFromRect(CGRectMake(self.view.frame.size.width/2, self.view.frame.size.height/4, 0, 0), inView: self.view, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true) }


Respuesta de Hardik Thakkar en Swift 5 , XCode 11 .

let quoteText = "Life is 10% what happens to you and 90% how you react to it." let activityViewController = UIActivityViewController(activityItems: [quoteText], applicationActivities: nil) if let popoverController = activityViewController.popoverPresentationController { popoverController.sourceRect = CGRect(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height / 2, width: 0, height: 0) popoverController.sourceView = self.view popoverController.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0) } UIApplication.topViewController?.present(activityViewController, animated: true, completion: nil)


Si muestra UIActivityViewController cuando hace clic en un UIBarButtonItem use el siguiente código:

activityViewController.popoverPresentationController?.barButtonItem = sender

De lo contrario, si usa otro control, por ejemplo, un UIButton , use el siguiente código:

activityViewController.popoverPresentationController?.sourceView = sender activityViewController.popoverPresentationController?.sourceRect = sender.bounds

De la documentación al UIPopoverPresentationController :

var barButtonItem: UIBarButtonItem? { get set }

Asigne un valor a esta propiedad para anclar el popover al elemento de botón de barra especificado. Cuando se presenta, la flecha del popover apunta al elemento especificado. Alternativamente, puede especificar la ubicación del ancla para el popover usando las propiedades sourceView y sourceRect.


Solución para Objective-C y con uso UIPopoverPresentationController

UIActivityViewController *controller = /*Init your Controller*/; //if iPhone if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { [self presentViewController:controller animated:YES completion:nil]; } //if iPad else { UIPopoverPresentationController* popOver = controller.popoverPresentationController if(popOver){ popOver.sourceView = controller.view; popOver.sourceRect = CGRectMake(self.view.frame.size.width/2, self.view.frame.size.height/4, 0, 0); [self presentViewController:controller animated:YES completion:nil]; } }


Solución usando Xamarin.iOS.

En mi ejemplo, estoy haciendo una captura de pantalla, produciendo una imagen y permitiendo que el usuario comparta la imagen. La ventana emergente en el iPad se coloca en el medio de la pantalla.

var activityItems = new NSObject[] { image }; var excludedActivityTypes = new NSString[] { UIActivityType.PostToWeibo, UIActivityType.CopyToPasteboard, UIActivityType.AddToReadingList, UIActivityType.AssignToContact, UIActivityType.Print, }; var activityViewController = new UIActivityViewController(activityItems, null); //set subject line if email is used var subject = new NSString("subject"); activityViewController.SetValueForKey(NSObject.FromObject("Goal Length"), subject); activityViewController.ExcludedActivityTypes = excludedActivityTypes; //configure for iPad, note if you do not your app will not pass app store review if(null != activityViewController.PopoverPresentationController) { activityViewController.PopoverPresentationController.SourceView = this.View; var frame = UIScreen.MainScreen.Bounds; frame.Height /= 2; activityViewController.PopoverPresentationController.SourceRect = frame; } this.PresentViewController(activityViewController, true, null);


Swift 3:

class func openShareActions(image: UIImage, vc: UIViewController) { let activityVC = UIActivityViewController(activityItems: [image], applicationActivities: nil) if UIDevice.current.userInterfaceIdiom == .pad { if activityVC.responds(to: #selector(getter: UIViewController.popoverPresentationController)) { activityVC.popoverPresentationController?.sourceView = vc.view } } vc.present(activityVC, animated: true, completion: nil) }


Swift, iOS 9/10 (después de que UIPopoverController en desuso)

let activityViewController = UIActivityViewController(activityItems: sharingItems, applicationActivities: nil) if UIDevice.currentDevice().userInterfaceIdiom == .Pad { if activityViewController.respondsToSelector(Selector("popoverPresentationController")) { activityViewController.popoverPresentationController?.sourceView = self.view } } self.presentViewController(activityViewController, animated: true, completion: nil)


Tenga cuidado si está desarrollando para iPad usando swift, funcionará bien en la depuración, pero se bloqueará en el lanzamiento. Para que funcione con testFlight y AppStore, desactive la optimización para -none usando -none para su lanzamiento.


Veo a muchas personas codificando iPhone / iPad, etc., mientras usan el código Swift.

Esto no es necesario, debe utilizar las funciones del idioma. El siguiente código supone que usará un UIBarButtonItem y funcionará tanto en iPhone como en iPad.

@IBAction func share(sender: AnyObject) { let vc = UIActivityViewController(activityItems: ["hello"], applicationActivities: nil) vc.popoverPresentationController?.barButtonItem = sender as? UIBarButtonItem self.presentViewController(vc, animated: true, completion: nil) }

Observe cómo no hay declaraciones If o cualquier otra cosa loca. El desempaquetado opcional será nulo en el iPhone, por lo que la línea vc.popoverPresentationController? no hará nada en iPhones.


swift = ios7 / ios8

let activityViewController = UIActivityViewController(activityItems: sharingItems, applicationActivities: nil) //if iPhone if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.Phone) { // go on.. } else { //if iPad if activityViewController.respondsToSelector(Selector("popoverPresentationController")) { // on iOS8 activityViewController.popoverPresentationController!.barButtonItem = self.shareButtonItem; } } self.presentViewController(activityViewController, animated: true, completion: nil)