Je teste actuellement mon application avec Xcode 6 (Beta 6). UIActivityViewController fonctionne bien avec les appareils iPhone et les simulateurs, mais tombe en panne avec les simulateurs iPad et les appareils (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 un crash similaire dans l'une de mes autres applications. Pouvez-vous me guider s'il vous plaît? UIActivityViewController sous iOS 8 a-t-il changé? J'ai vérifié mais je n'ai rien trouvé à ce sujet
Sur iPad, le contrôleur de vue d'activité s'affiche sous la forme d'un popover à l'aide du nouveau UIPopoverPresentationController , vous devez donc spécifier un point d'ancrage pour la présentation du popover à l'aide de l'une des trois propriétés suivantes:
Pour spécifier le point d'ancrage, vous devez obtenir une référence à UIPopoverPresentationController 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 UIActivityViewController dans 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"];
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 4.2
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 = [ UIActivity.ActivityType.airDrop]
//vwShareVideo is a view from which you want to show popover in iPad
if UIDevice.current.userInterfaceIdiom == .pad {
activityViewController.popoverPresentationController?.sourceView = self.vwShareVideo
activityViewController.popoverPresentationController?.permittedArrowDirections = .down
activityViewController.popoverPresentationController?.sourceRect = self.vwShareVideo.bounds
}
self.present(activityViewController, animated: true, completion: nil)
}
Je rencontrais ce problème précis récemment (la question initiale) dans Swift 2.0, où UIActivityViewController
fonctionnait bien pour les iPhones, mais provoquait des plantages lors de la simulation d'iPad.
Je veux simplement ajouter à ce fil de réponses ici que, du moins dans Swift 2.0, vous n'avez pas besoin d'une instruction if. Vous pouvez simplement rendre la popoverPresentationController
optionnelle.
En bref, 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 de l'un des éléments suivants:
L'exemple particulier sur lequel je travaillais est ci-dessous, où je crée une fonction qui prend un UIView
(pour le sourceView et sourceRect) et un String
(l'unique activitéItem 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ées.
Kinda Nice, tout ce que vous devez faire pour que cela fonctionne pour les iPads, c'est simplement ajouter deux lignes de code, ou juste une si vous utilisez un objet barButtonItem!
Je vois beaucoup de gens coder en dur iPhone/iPad, etc. tout 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 les deux 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 comme il n’ya pas de déclarations If ou autre chose folle. Le décompactage 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'utilisateur de partager l'image. La fenêtre pop-up sur l'iPad est placée 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 que UIPopoverController soit déconseillé)
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 je l'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)
}
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)
}
Si vous affichez UIActivityViewController
lorsque vous cliquez sur une UIBarButtonItem
, utilisez le code suivant:
activityViewController.popoverPresentationController?.barButtonItem = sender
Sinon, si vous utilisez un autre contrôle, par exemple un UIButton
, utilisez le code suivant:
activityViewController.popoverPresentationController?.sourceView = sender
activityViewController.popoverPresentationController?.sourceRect = sender.bounds
De la documentation à la UIPopoverPresentationController
:
var barButtonItem: UIBarButtonItem? { get set }
Attribuez une valeur à cette propriété pour ancrer la popover à l'élément de bouton à barres spécifié. Lorsqu'elle est présentée, la flèche de la souris popover pointe sur l'élément spécifié. Vous pouvez également spécifier l'emplacement d'ancrage du popover à l'aide des propriétés sourceView et sourceRect.
Solution pour Objective-C et avec utilisation 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];
}
}
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)
Dans Swift 4, le code suivant fonctionne dans iphone et ipad . Selon la documentation
Il est de votre responsabilité de présenter et de rejeter le contrôleur de vue à l'aide des moyens appropriés pour l'idiome de périphérique donné. Sur iPad, vous devez présenter le contrôleur de vue dans une fenêtre popover. Sur d'autres appareils, vous devez le présenter sous forme modale.
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)
J'ai trouvé cette solution Premièrement, 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 la 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;
}