UIActivityViewController stürzt auf iOS 8 iPads ab

288

Ich teste derzeit meine App mit Xcode 6 (Beta 6). UIActivityViewController funktioniert gut mit iPhone-Geräten und -Simulatoren, stürzt jedoch mit iPad-Simulatoren und -Geräten (iOS 8) mit folgenden Protokollen ab

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

Ich verwende den folgenden Code für iPhone und iPad sowohl für iOS 7 als auch für 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];

Ich bekomme einen ähnlichen Absturz in einer meiner anderen Apps. Kannst du mich bitte führen? Hat sich mit UIActivityViewController in iOS 8 etwas geändert? Ich habe nachgesehen, aber nichts gefunden

Bhumit Mehta
quelle
Antworten unter Test für die Redewendung. Sie sollten die Antwort von @ Galen verwenden, die dies nicht tut.
DoozMen

Antworten:

462

Auf dem iPad wird der Aktivitätsansichts-Controller mit dem neuen UIPopoverPresentationController als Popover angezeigt. Dazu müssen Sie einen Ankerpunkt für die Darstellung des Popovers mit einer der drei folgenden Eigenschaften angeben:

Um den Ankerpunkt anzugeben, müssen Sie einen Verweis auf den UIPopoverPresentationController des UIActivityController erhalten und eine der folgenden Eigenschaften festlegen:

if ( [activityViewController respondsToSelector:@selector(popoverPresentationController)] ) { 
// iOS8
 activityViewController.popoverPresentationController.sourceView =
parentView;
 }
mmccomb
quelle
4
@Daljeet Eine einfache Möglichkeit wäre, eine transparente 1x1-Ansicht an einer beliebigen Stelle zu platzieren, an der der Punkt des Popovers angezeigt werden soll, und diese als Ankeransicht zu verwenden.
Mason Cloud
3
@Danke mmccomb, ich habe Ihren Code oben angewendet, er funktioniert gut unter iOS 8, aber meine App stürzt unter iOS 7 ab. Ich habe das gleiche Problem bei stackoverflow gepostet. Hier ist der Link: stackoverflow.com/questions/26034149/… Hilfe wird geschätzt
Daljeet
48
@ Daljeet Dies wird unter iOS7 abstürzen, da UIActivityController dort keine Eigenschaft popoverPresentationController hat. Verwenden Sie diesen Code, damit es unter iOS8 und iOS7 funktioniert: if ( [activityViewController respondsToSelector:@selector(popoverPresentationController)] ) { // iOS8 activityViewController.popoverPresentationController.sourceView = _shareItem; }
bluebamboo
4
In iOS8 hat sourceRect bei mir nicht funktioniert. Ich musste mit diesem Rect eine SouceView erstellen, und das hat gut funktioniert.
Harris
10
@bluebamboo Warum fügst du deinen Vorschlag nicht als Bearbeitung zur Antwort hinzu? Es ist ziemlich leicht, Ihre wichtigen Informationen in diesen Kommentaren zu übersehen.
Ayush Goel
203

Das gleiche Problem ist bei meinem Projekt aufgetreten, dann habe ich die Lösung gefunden, um das zu öffnen UIActivityViewController wir iPad verwenden müssenUIPopoverController

Hier ist ein Code für die Verwendung in iPhone und 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];
}

Für schnelles 4.2 / schnelles 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)
}
Hardik Thakkar
quelle
5
Für "neuere" Swift-Syntax sollte UIPopoverArrowDirectionAny UIPopoverArrowDirection.Any sein und UIUserInterfaceIdiomPhone ist UIUserInterfaceIdiom.Phone
EPage_Ed
1
Vielen Dank, dies funktioniert zusammen mit den Kommentaren von EPage_Ed unter XCode 7 und SWIFT 2. Ich habe es positiv bewertet.
Oliver Zhang
1
UIPopoverController ist mit iOS 9 veraltet.
cbartel
3
UIPopOverController ist in iOS 9 veraltet.
Leena
1
Jeder sollte auf diese Weise antworten, da es einfacher ist, nach ios-Antworten zu suchen als nach schnellen. Danke, Mann, du hast meinen Tag gemacht.
MBH
42

Ich habe kürzlich genau dieses Problem (die ursprüngliche Frage) in Swift 2.0 festgestellt, wo UIActivityViewController für iPhones gut funktionierte, aber bei der Simulation von iPads Abstürze verursachte.

Ich möchte diesem Thread hier nur hinzufügen, dass Sie zumindest in Swift 2.0 keine if-Anweisung benötigen. Sie können einfach die machenpopoverPresentationController optionale machen.

Abgesehen davon scheint die akzeptierte Antwort zu lauten, dass Sie nur eine Quellansicht, nur eine Quellrektive oder nur ein barButtonItem haben könnten. Laut Apples Dokumentation für UIPopoverPresentationController benötigen Sie jedoch eine der folgenden Optionen :

  • barButtonItem
  • sourceView und sourceRect

Das spezielle Beispiel, an dem ich gearbeitet habe, ist unten, wo ich eine Funktion erstelle, die ein UIView(für sourceView und sourceRect) und String(das einzige activityItem des UIActivityViewController) enthält.

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

Dieser Code funktioniert auf iPhone und iPad (und sogar auf tvOS, glaube ich) - wenn das Gerät dies nicht unterstützt popoverPresentationController , werden die beiden Codezeilen, in denen es erwähnt wird, im Wesentlichen ignoriert.

Ein bisschen schön, dass alles, was Sie tun müssen, damit es für iPads funktioniert, nur zwei Codezeilen hinzufügt, oder nur eine, wenn Sie ein barButtonItem verwenden!

Galen
quelle
2
Diese Lösung scheint viel sauberer zu sein als die anderen Lösungen, die verwendet werden müssen, respondsToSelectoroder UIUserInterfaceIdiomsie scheint auch viel besser zu Swift als Sprache zu passen. Obwohl respondsToSelectordies für iOS7 notwendig erscheint, scheint dies bei Verwendung neuerer Versionen von iOS definitiv der richtige Weg zu sein.
Marcus
18

Ich sehe viele Leute, die iPhone / iPad usw. fest codieren, während sie Swift-Code verwenden.

Dies ist nicht erforderlich, Sie müssen die Sprachfunktionen verwenden. Der folgende Code setzt voraus, dass Sie ein UIBarButtonItem verwenden und sowohl auf dem iPhone als auch auf dem iPad funktionieren .

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

Beachten Sie, dass es keine If-Anweisungen oder andere verrückte Dinge gibt. Das optionale Auspacken ist auf dem iPhone gleich Null, sodass die Leitung vc.popoverPresentationController?auf iPhones nichts bewirkt.

Martin Marconcini
quelle
Wie würde das im Kontext dieses Tutorials aussehen
Dave Kliman
1
In diesem Tutorial wird der popoverPresentationController nicht erwähnt, sodass der Code auf dem iPad iOS 9.x abstürzt. Die Lösung wäre, hinzuzufügen, vc.popoverPresentationController?.barButtonItem = navigationItem.rightBarButtonItembevor Sie den Ansichts-Controller präsentieren.
Martin Marconcini
Hallo Martin ... Ich hatte schon eine Zeile, die so aussah, in shareTapped(): vc.popoverPresentationController?.barButtonItem = sender as? UIBarButtonItembeide stürzten immer noch ab. Was mache ich falsch? Wie fülle ich das Popover mit den verschiedenen Freigabediensten wie Facebook, Twitter usw. auf, während ich hier bin?
Dave Kliman
Und was ist der Absturz? Vielleicht möchten Sie Ihre eigene Frage stellen. Überprüfen Sie, ob dies sendertatsächlich ein ist UIBarButtonItem. Überprüfen Sie, ob vc nicht ist nil. Überprüfen Sie, ob Sie einen ViewController präsentieren können ... ohne auf den Absturz / Code zu achten, ist schwer zu sagen. Die Dienste werden automatisch ausgefüllt, wenn der Benutzer die App installiert hat (es sei denn, Sie haben ausdrücklich beschlossen, einige auszuschließen).
Martin Marconcini
1
@ DaveKliman Ja, Xcode ist sehr schlecht, wenn es um IBOutlets und IBActions geht, die entfernt / umbenannt wurden, und es stürzt beim Start ab. Es gibt wirklich keinen anderen Weg, dies unter iOS zu tun. Sie müssen Xcode und InterfaceBuilder verwenden. Sie können viele Dinge "im Code" tun, aber einige erfordern das "Ziehen und Ablegen". Apple möchte, dass Sie Storyboards und InterfaceBuilder so oft wie möglich verwenden.
Gewöhnen
10

Lösung mit Xamarin.iOS.

In meinem Beispiel mache ich eine Bildschirmaufnahme, erstelle ein Bild und erlaube dem Benutzer, das Bild freizugeben. Das Popup auf dem iPad befindet sich ungefähr in der Mitte des Bildschirms.

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);
ben
quelle
Vielen Dank :) arbeitet mit Xamarin Forms unter Verwendung eines Abhängigkeitsdienstes
Ricardo Romo
7

Swift, iOS 9/10 (nachdem UIPopoverController veraltet ist)

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)
MPaulo
quelle
3
Swift 3 unterstützt dies nicht
Maksim Kniazev
5

In Swift, um dies für das iPad zu beheben, ist es am besten, dies so zu tun, wie ich es gefunden habe.

    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
    }
Niklas
quelle
Verwenden Sie die Antwort von @Galen. Kein Problem für die Überprüfung der Redewendung beim Targeting von iOS8 +
doozMen
5

Wenn Sie UIActivityViewControllerbeim Klicken auf einen anzeigen, UIBarButtonItemverwenden Sie den folgenden Code:

activityViewController.popoverPresentationController?.barButtonItem = sender

Wenn Sie andernfalls ein anderes Steuerelement verwenden, z. B. a UIButton, verwenden Sie den folgenden Code:

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

Von der Dokumentation bis zum UIPopoverPresentationController:

var barButtonItem: UIBarButtonItem? { get set }

Weisen Sie dieser Eigenschaft einen Wert zu, um das Popover an dem angegebenen Balkenschaltflächenelement zu verankern. Wenn angezeigt, zeigt der Pfeil des Popovers auf das angegebene Element. Alternativ können Sie den Ankerort für das Popover mithilfe der Eigenschaften sourceView und sourceRect angeben.

DronPop
quelle
4

Fix für 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)
    }
datayeah
quelle
2
UIPopoverController ist veraltet.
LevinsonTechnologies
1
Danke dir!! Es hat mir sehr geholfen.
Oscar
4

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

Schnell:

    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)

    }
Kalpeshdeo
quelle
2

Lösung für Objective-C und mit 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];
        }
    }
kurono267
quelle
1

Ich habe den nächsten Code ausprobiert und es funktioniert:

Fügen Sie zuerst ein Balkenschaltflächenelement in Ihren View Controller ein und erstellen Sie dann ein IBOutlet:

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

Weiter in der .m-Datei: yourUIActivityViewController.popoverPresentationController.barButtonItem = self.barButtonItem;

Mike
quelle
1

schnell = 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)
ingconti
quelle
0

Ich habe diese Lösung gefunden. Erstens sollte Ihr View Controller, der das Popover präsentiert, das implementieren <UIPopoverPresentationControllerDelegate> .

Als nächstes müssen Sie die einstellen popoverPresentationController Delegaten des .

Fügen Sie diese Funktionen hinzu:

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

In Swift 4 funktioniert der folgende Code in iPhone und iPad. Entsprechend Dokumentation

Es liegt in Ihrer Verantwortung, den View Controller mit den für die angegebene Gerätesprache geeigneten Mitteln zu präsentieren und zu schließen. Auf dem iPad müssen Sie den View Controller in einem Popover anzeigen. Auf anderen Geräten müssen Sie es modal präsentieren.

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

Ich benutze Swift 5. Ich hatte das gleiche Problem mit dem Absturz, als ich in meiner App auf dem iPad auf "Teilen" klickte. Fand eine dieser Lösung. Schritt 1: Fügen Sie dem Main.storyboard das Objekt "view" hinzu (suchen Sie in der Objektbibliothek nach "UIView"). Schritt 2: Erstellen Sie ein @IBOutlet in ViewController.swift und weisen Sie einen beliebigen Namen zu (z. B. view1).

Schritt 3: Fügen Sie den obigen Namen (z. B. view1) als Quellansicht hinzu. Dies ist meine Aktion "Teilen".

@IBAction func Share(_ sender: Any) {
    let activityVC = UIActivityViewController(activityItems: ["www.google.com"], applicationActivities: nil)
    activityVC.popoverPresentationController?.sourceView = view1

    self.present(activityVC, animated: true, completion: nil)


}

Ich bin sehr neu in Swift und war eine Woche lang dabei. hoffe, das wird jemandem helfen. Teilen Sie diese Lösung.

Dishara
quelle
Danke das hat funktioniert!
Jnguyen22 vor
-1

Für Swift 2.0. Ich habe festgestellt, dass dies funktioniert, wenn Sie versuchen, das Popover an einer Freigabeschaltfläche auf dem iPad zu verankern. Dies setzt voraus, dass Sie in Ihrer Symbolleiste einen Ausgang für die Freigabeschaltfläche erstellt haben.

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

    }
}
iosdevlangley
quelle
-5

Seien Sie vorsichtig, wenn Sie mit swift für das iPad entwickeln. Es funktioniert beim Debuggen einwandfrei, stürzt jedoch beim Release ab. Deaktivieren Sie die Optimierung für die schnelle Verwendung -nonefür die Freigabe , damit sie mit testFlight und AppStore funktioniert .

ingconti
quelle