web-dev-qa-db-fra.com

UIActivityViewController se bloque sur les iPads iOS 8

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

267
Bhumit Mehta

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;
 }
447
mmccomb

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)
    }
175
Hardik Thakkar

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:

  • 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 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!

36
Galen

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.

16
Martin Marconcini

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);
10
ben

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

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
    }
5
Niklas

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

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)
}
4
Daniel McLean

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)

    }
3
kalpeshdeo

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.

2
DronPop

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];
        }
    }
1
kurono267

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)
1
ingconti

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)
0
Imran Khan

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;
}
0
Mongo db