Le crash de UIActivityViewController sur iPads iOS8

Je suis actuellement en train de tester mon application avec XCode 6 (Beta 6). UIActivityViewController fonctionne correctement avec les appareils et les simulateurs iPhone, mais se bloque avec les simulateurs et appareils iPad (iOS 8) avec les journaux suivants

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

J'utilise le code suivant pour iPhone et iPad pour iOS 7 ainsi que 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]; 

Je reçois aussi un accident similaire d'une de mes autres applications. Pouvez-vous me guider s'il vous plaît? a quelque chose a changé avec UIActivityViewController dans iOS 8? J'ai vérifié mais je n'ai rien trouvé sur ce

Sur iPad, le controller de vue d'activité s'affichera en tant que popover à l'aide du nouveau UIPopoverPresentationController . Vous devrez spécifier un point d'ancrage pour la présentation du popover en utilisant l'une des trois propriétés suivantes:

  • barButtonItem
  • sourceView
  • sourceRect

Pour spécifier le point d'ancrage, vous devez get une reference au UIPopoverPresentationController du UIActivityController et définir l'une des propriétés comme suit:

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

Le même problème est venu à mon projet puis j'ai trouvé la solution que pour ouvrir le UIActivityViewController dans l' iPad, nous devons utiliser UIPopoverController

Voici un code pour l'utiliser sur iPhone et iPad

 //to attach the image and text with sharing UIImage *image=[UIImage imageNamed:@"giraffe.png"]; NSSsortingng *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]; } 

Pour Swift 3

 let imageURL: URL = URL(ssortingng: product.images[0].fullImageUrlSsortingng)! let objectsToShare: [AnyObject] = [imageURL as AnyObject] let activityViewController = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil) activityViewController.popoverPresentationController?.sourceView = self.view activityViewController.excludedActivityTypes = [ UIActivityType.airDrop] self.present(activityViewController, animated: true, completion: nil) 

Je rencontrais ce problème précis récemment (la question originale) dans Swift 2.0, où UIActivityViewController a bien fonctionné pour les iPhones, mais a causé des plantages lors de la simulation des iPads.

Je veux juste append à ce fil de réponses ici que, au less dans Swift 2.0, vous n'avez pas besoin d'une instruction if. Vous pouvez simplement rendre le popoverPresentationController facultatif.

En passant, la réponse acceptée semble être que vous pourriez avoir juste un sourceView, juste un sourceRect, ou juste un barButtonItem, mais selon la documentation d' Apple pour UIPopoverPresentationController, vous avez besoin d'un des éléments suivants:

  • barButtonItem
  • sourceView et sourceRect

L'exemple particulier sur lequel je travaillais est ci-dessous, où je crée une fonction qui UIView un UIView (pour le sourceView et sourceRect) et Ssortingng (le seul activityItem de UIActivityViewController).

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

Ce code fonctionne sur iPhone et iPad (et même tvOS je pense) – si l'appareil ne supporte pas popoverPresentationController , les deux lignes de code qui le mentionnent sont essentiellement ignorées.

Kinda sympa que tout ce que vous devez faire pour le faire fonctionner pour les iPads est juste append deux lignes de code, ou juste un si vous utilisez un barButtonItem!

Je vois beaucoup de gens codant en dur iPhone / iPad etc. en utilisant le code Swift.

Ce n'est pas nécessaire, vous devez utiliser les fonctionnalités du langage. Le code suivant suppose que vous utiliserez un UIBarButtonItem et fonctionnera sur iPhone et 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) } 

Remarquez qu'il n'y a pas d'instructions If ou d'autres choses folles. Le déballage optionnel sera nul sur iPhone, donc la ligne vc.popoverPresentationController? ne fera rien sur les iPhones.

Solution utilisant Xamarin.iOS.

Dans mon exemple, je fais une capture d'écran, produisant une image, et permettant à l'user de partager l'image. La pop up sur l'iPad est placée à peu près au milieu de l'écran.

 var activityItems = new NSObject[] { image }; var excludedActivityTypes = new NSSsortingng[] { 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 NSSsortingng("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, iOS 9/10 (après UIPopoverController déprécié)

 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) 

Dans Swift pour résoudre ce problème pour iPad, le meilleur moyen est de faire comme ça que j'ai trouvé.

  let things = ["Things to share"] let avc = UIActivityViewController(activityItems:things, applicationActivities:nil) avc.setValue("Subject title", forKey: "subject") avc.completionWithItemsHandler = { (s: Ssortingng!, 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 } 

Correction de 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) } 

Rapide:

  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) } 

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 = 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) 

J'ai trouvé cette solution Premièrement, votre controller de vue qui présente le popover devrait implémenter le protocole <UIPopoverPresentationControllerDelegate> .

Ensuite, vous devrez définir le popoverPresentationController de popoverPresentationController .

Ajoutez ces fonctions:

 - (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 isEqualToSsortingng:@"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; } 

J'ai essayé le code suivant et cela fonctionne:

mettez d'abord un élément de barre dans votre View Controller puis créez un IBOutlet:

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

suivant dans le file yourUIActivityViewController.popoverPresentationController.barButtonItem = self.barButtonItem; : yourUIActivityViewController.popoverPresentationController.barButtonItem = self.barButtonItem;

Pour Swift 2.0. J'ai trouvé que cela fonctionne si vous essayez d'ancrer le popover à un button de partage sur iPad. Cela suppose que vous avez créé une sortie pour le button de partage dans votre barre d'outils.

 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) } } } 

Soyez prudent si vous développez pour l'iPad en utilisant swift, cela fonctionnera très bien dans le debugging, mais se bloquera dans la version. Pour le faire fonctionner avec testFlight et AppStore, désactivez l'optimization pour swift en utilisant -none pour la version.