UIActivityViewController crashing on iOS 8 iPads

IosObjective CSwiftUiactivityviewcontroller

Ios Problem Overview


I am currently testing my app with Xcode 6 (Beta 6). UIActivityViewController works fine with iPhone devices and simulators but crashes with iPad simulators and devices (iOS 8) with following logs

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

I am using following code for iPhone and iPad for both iOS 7 as well as 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];

I am getting a similar crash in of one my other app as well. Can you please guide me ? has anything changed with UIActivityViewController in iOS 8? I checked but i did not find anything on this

Ios Solutions


Solution 1 - Ios

On iPad the activity view controller will be displayed as a popover using the new UIPopoverPresentationController, it requires that you specify an anchor point for the presentation of the popover using one of the three following properties:

In order to specify the anchor point you will need to obtain a reference to the UIActivityController's UIPopoverPresentationController and set one of the properties as follows:

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

Solution 2 - Ios

Same problem is come to my project then i found the solution that to open the UIActivityViewController in iPad we have to use UIPopoverController

Here is a code to use it in iPhone and iPad both:

//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];
}

For swift 4.2 / swift 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)
}

Solution 3 - Ios

I was encountering this exact problem recently (the original question) in Swift 2.0, where UIActivityViewController worked fine for iPhones, but caused crashes when simulating iPads.

I just want to add to this thread of answers here that, at least in Swift 2.0, you don't need an if statement. You can just make the popoverPresentationController optional.

As a quick aside, the accepted answer appears to be saying that you could have just a sourceView, just a sourceRect, or just a barButtonItem, but according to Apple's documentation for UIPopoverPresentationController you need one of the following:

  • barButtonItem
  • sourceView and sourceRect

The particular example I was working on is below, where I am creating a function that takes in a UIView (for the sourceView and sourceRect) and String (the UIActivityViewController's sole activityItem).

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

This code works on iPhone and iPad (and even tvOS I think) -- if the device does not support popoverPresentationController, the two lines of code that mention it are essentially ignored.

Kinda nice that all you need to do to make it work for iPads is just add two lines of code, or just one if you're using a barButtonItem!

Solution 4 - Ios

I see a lot of people hardcoding iPhone/iPad etc. while using Swift code.

This is not needed, you have to use the language features. The following code assumes you will use a UIBarButtonItem and will work on both iPhone and 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)
 }

Notice how there are no If statements or any other crazy thing. The optional unwrapping will be nil on iPhone, so the line vc.popoverPresentationController? will not do anything on iPhones.

Solution 5 - Ios

Solution using Xamarin.iOS.

In my example I'm doing a screen capture, producing an image, and allowing the user to share the image. The pop up on the iPad is placed about in the middle of the screen.

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

Solution 6 - Ios

Swift, iOS 9/10 (after UIPopoverController deprecated)

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)

Solution 7 - Ios

If you show UIActivityViewController when you click on an UIBarButtonItem use the following code:

activityViewController.popoverPresentationController?.barButtonItem = sender

Otherwise, if you use another control, for example a UIButton, use the following code:

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

From the documentation to the UIPopoverPresentationController:

var barButtonItem: UIBarButtonItem? { get set }

> Assign a value to this property to anchor the popover to the specified bar button item. When presented, the popover’s arrow points to the specified item. Alternatively, you may specify the anchor location for the popover using the sourceView and sourceRect properties.

Solution 8 - Ios

In Swift to fix this for iPad, best way is to do like this I found.

    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
    }

Solution 9 - Ios

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

Solution 10 - Ios

SwiftUI Version

func presentActivityView(items: [Any]){
    let activityController = UIActivityViewController(activityItems: items, applicationActivities: nil)
    if UIDevice.current.userInterfaceIdiom == .pad{
        activityController.popoverPresentationController?.sourceView = UIApplication.shared.windows.first
        activityController.popoverPresentationController?.sourceRect = CGRect(x:  UIScreen.main.bounds.width / 3, y:  UIScreen.main.bounds.height / 1.5, width: 400, height: 400)
    }
    UIApplication.shared.windows.first?.rootViewController?.present(activityController, animated: true, completion: nil)
}

Solution 11 - Ios

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

Solution 12 - Ios

Solution for Objective-C and with use 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];
        }
    }

Solution 13 - Ios

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

Solution 14 - Ios

In swift 4 following code working in iphone and ipad. According documentation

> It is your responsibility to present and dismiss the view controller using the appropriate means for the given device idiom. On iPad, you must present the view controller in a popover. On other devices, you must present it modally.

 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)

Solution 15 - Ios

I tried the next code and it works:

first put a bar button item in your View Controller then create an IBOutlet:

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

next in the .m file: yourUIActivityViewController.popoverPresentationController.barButtonItem = self.barButtonItem;

Solution 16 - Ios

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)

Solution 17 - Ios

Im using Swift 5. I had the same issue of crashing when I click "Share button" in my app on iPad. Found a this solution. step 1: Add "view" object ( search "UIView" in the object library) to the Main.storyboard. step 2: Create a @IBOutlet in ViewController.swift and assign any name( eg: view1)

step 3: add the above name ( eg: view1) as the sourceView. this is my "Share button" action.

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

I am very new to swift and was stuck on this for a week. hope this will help someone. so sharing this solution.

Solution 18 - Ios

I found this solution Firstly, your view controller that's presenting the popover should implement the <UIPopoverPresentationControllerDelegate> protocol.

Next, you'll need to set the popoverPresentationController's delegate.

Add these functions:

- (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;
}

Solution 19 - Ios

best solve this problem

let urlstring = "https://apps.apple.com/ae/app/"
let text = "some text for your app"
let url = NSURL(string: urlstring)
let textToShare = [url!,text] as [Any]
let activityViewController = UIActivityViewController(activityItems: textToShare as [Any], applicationActivities: nil)
    
activityViewController.excludedActivityTypes = [ UIActivity.ActivityType.airDrop, UIActivity.ActivityType.postToFacebook ,UIActivity.ActivityType.postToFlickr,UIActivity.ActivityType.postToTwitter,UIActivity.ActivityType.postToVimeo,UIActivity.ActivityType.mail,UIActivity.ActivityType.addToReadingList]

activityViewController.popoverPresentationController?.sourceView = self.view
activityViewController.popoverPresentationController?.sourceRect = view.bounds
activityViewController.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.down
UIApplication.shared.windows.first?.rootViewController?.present(activityViewController, animated: true, completion: nil)

Solution 20 - Ios

For Swift 2.0. I found that this works if you are trying to anchor the popover to a share button on iPad. This assumes that you have created an outlet for the share button in your tool bar.

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

Solution 21 - Ios

Be careful if you are developing for iPad using swift, it will work fine in debug, but will crash in release. In order to make it work with testFlight and AppStore, disable optimization for swift using -none for release.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBhumit MehtaView Question on Stackoverflow
Solution 1 - IosmmccombView Answer on Stackoverflow
Solution 2 - IosHardik ThakkarView Answer on Stackoverflow
Solution 3 - IosGalenView Answer on Stackoverflow
Solution 4 - IosMartin MarconciniView Answer on Stackoverflow
Solution 5 - IosbenView Answer on Stackoverflow
Solution 6 - IosMPauloView Answer on Stackoverflow
Solution 7 - IosdronpopdevView Answer on Stackoverflow
Solution 8 - IosNiklasView Answer on Stackoverflow
Solution 9 - IosDaniel McLeanView Answer on Stackoverflow
Solution 10 - IosbatuhankrbbView Answer on Stackoverflow
Solution 11 - Iosdy_View Answer on Stackoverflow
Solution 12 - Ioskurono267View Answer on Stackoverflow
Solution 13 - IoskalpeshdeoView Answer on Stackoverflow
Solution 14 - IosImran KhanView Answer on Stackoverflow
Solution 15 - IosMikeView Answer on Stackoverflow
Solution 16 - IosingcontiView Answer on Stackoverflow
Solution 17 - IosDisharaView Answer on Stackoverflow
Solution 18 - IosMongo dbView Answer on Stackoverflow
Solution 19 - Iosislam XDeveloperView Answer on Stackoverflow
Solution 20 - IosiosdevlangleyView Answer on Stackoverflow
Solution 21 - IosingcontiView Answer on Stackoverflow