Uiactivityviewcontroller se bloque sur iOS 8 iPads
Je teste actuellement mon application avec Xcode 6 (Beta 6). UIActivityViewController fonctionne bien avec les appareils et les simulateurs iPhone, mais se bloque avec les simulateurs et les 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 pour 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];
Je reçois également un crash similaire dans l'une de mes autres applications. Pouvez-vous svp me guider ? quelque chose a-t-il changé avec UIActivityViewController dans iOS 8? J'ai vérifié mais je n'ai pas trouvé quelque chose à ce sujet
15 réponses
Sur iPad, le contrôleur de vue d'activité sera affiché en tant que popover en utilisant le nouveau UIPopoverPresentationController , Il nécessite que vous spécifiez un point d'ancrage pour la présentation du popover en utilisant l'une des trois propriétés suivantes:
Pour spécifier le point d'ancrage, vous devez obtenir une référence à UIActivityController UIPopoverPresentationController et définissez l'une des propriétés comme suit:
if ( [activityViewController respondsToSelector:@selector(popoverPresentationController)] ) {
// iOS8
activityViewController.popoverPresentationController.sourceView =
parentView;
}
Même problème est arrivé à mon projet, puis j'ai trouvé la solution pour ouvrir la UIActivityViewController dans iPad, nous devons utiliser UIPopoverController
Voici un code pour l'utiliser dans iPhone et iPad à la fois
//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];
}
Pour swift
let imageURL: URL = URL(string: product.images[0].fullImageUrlString)!
let objectsToShare: [AnyObject] = [imageURL as AnyObject]
let activityViewController = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view
activityViewController.excludedActivityTypes = [.airDrop]
self.present(activityViewController, animated: true, completion: nil)
Je rencontrais ce problème exact récemment (la question originale) dans Swift 2.0, où UIActivityViewController
fonctionnait bien pour les iPhones, mais provoquait des plantages lors de la simulation d'iPads.
Je veux juste ajouter à ce fil de réponses ici que, au moins dans Swift 2.0, vous n'avez pas besoin d'une instruction if. Vous pouvez simplement rendre le popoverPresentationController
facultatif.
En bref, la réponse acceptée semble dire 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 de l'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 prend un UIView
(pour sourceView et sourceRect) et String
(le seul 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)
}
Ce code fonctionne sur iPhone et iPad (et même tvOS je pense) -- si le périphérique ne prend pas en charge popoverPresentationController
, les deux lignes de code qui le mentionnent sont essentiellement ignoré.
Un peu agréable que tout ce que vous devez faire pour le faire fonctionner pour iPads est juste ajouter deux lignes de code, ou juste une si vous utilisez un barButtonItem!
Je vois beaucoup de gens codant en dur iPhone / iPad, etc. tout en utilisant le code Swift.
Ce n'est pas nécessaire, vous devez utiliser les fonctionnalités de langue. Le code suivant suppose que vous utiliserez un UIBarButtonItem et fonctionnera sur à la fois 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 comment il n'y a pas de déclarations If ou toute autre chose folle. 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, j'ai fais une capture d'écran, produisant une image, et permettant à l'utilisateur de partager l'image. Le pop-up sur l'iPad est placé au milieu de l'écran.
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, iOS 9/10 (après uipopovercontroller obsolète)
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, la meilleure façon est de faire comme ceci 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: 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
}
Correctif pour 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)
}
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:
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 = 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
Tout d'abord, votre contrôleur de vue qui présente le popover devrait implémenter le protocole <UIPopoverPresentationControllerDelegate>
.
Ensuite, vous devrez définir le délégué 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 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;
}
J'ai essayé le code suivant et ça fonctionne:
Mettez D'abord un élément de bouton de barre dans votre contrôleur de vue ensuite, créez un IBOutlet:
@property(weak,nonatomic)IBOutlet UIBarButtonItem *barButtonItem;
Suivant dans le .m fichier: yourUIActivityViewController.popoverPresentationController.barButtonItem = self.barButtonItem;
Pour Swift 2.0. J'ai trouvé que cela fonctionne si vous essayez d'ancrer le popover à un bouton de partage sur iPad. Cela suppose que vous avez créé une sortie pour le bouton Partager 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 iPad en utilisant swift, il fonctionnera bien dans le débogage, mais Plantera dans la version. Pour le faire fonctionner avec TestFlight et AppStore, désactivez l'optimisation pour swift en utilisant -none
pour la version.