Sharing button works perfectly on iPhone but crash on iPad - swift

I'm trying to add a button in order to share some sentences in Twitter, Facebook... etc. It all works on all iPhone models but simulator crash with an iPad.
This is my code:
#IBAction func shareButton(sender: AnyObject) {
frase = labelFrases.text!
autor = labelAutores.text!
var myShare = "\(frase) - \(autor)"
let activityVC: UIActivityViewController = UIActivityViewController(activityItems: [myShare], applicationActivities: nil)
self.presentViewController(activityVC, animated: true, completion: nil)
And this is the error:
Terminating app due to uncaught exception 'NSGenericException', reason: 'UIPopoverPresentationController (<_UIAlertControllerActionSheetRegularPresentationController: 0x7c0f9190>) should have a non-nil sourceView or barButtonItem set before the presentation occurs
How should I solve it?

For ipad (iOS > 8.0) you need to set popoverPresentationController:
//check ipad
if (UIDevice.currentDevice().userInterfaceIdiom == UIUserInterfaceIdiom.Pad)
{
//ios > 8.0
if ( activityVC.respondsToSelector(Selector("popoverPresentationController"))){
activityVC.popoverPresentationController?.sourceView = super.view
}
}
self.presentViewController(activityVC, animated: true, completion: nil)
More information here:
UIActivityViewController crashing on iOS 8 iPads

Do this instead for Swift 5 to get share button working on both iPad and iPhone:
#IBAction func shareButton(sender: UIButton) { {
let itemToShare = ["Some Text goes here"]
let avc = UIActivityViewController(activityItems: itemToShare, applicationActivities: nil)
//Apps to be excluded sharing to
avc.excludedActivityTypes = [
UIActivityType.print,
UIActivityType.addToReadingList
]
// Check if user is on iPad and present popover
if UIDevice.current.userInterfaceIdiom == .pad {
if avc.responds(to: #selector(getter: UIViewController.popoverPresentationController)) {
avc.popoverPresentationController?.barButtonItem = sender
}
}
// Present share activityView on regular iPhone
self.present(avc, animated: true, completion: nil)
}
Hope this helps!

Slightly adapted version to make it work on any button, iPad and iPhone.
Xcode 13.4.1 (Swift 5.6)
let itemToShare = ["Some Text goes here"]
let avc = UIActivityViewController(activityItems: itemToShare, applicationActivities: nil)
//Apps to be excluded sharing to
avc.excludedActivityTypes = [
UIActivity.ActivityType.print,
UIActivity.ActivityType.addToReadingList
]
// Check if user is on iPad and present popover
if UIDevice.current.userInterfaceIdiom == .pad {
if avc.responds(to: #selector(getter: UIViewController.popoverPresentationController)) {
avc.popoverPresentationController?.sourceView = sender as? UIView
}
}
// Present share activityView on regular iPhone
self.present(avc, animated: true, completion: nil)

Related

UIActivityViewController becomes blank on iOS 15

I have an iOS App similar to Photos App from Apple, which also has a ‘Share’ button for sharing photo, which worked well before. The code is as follows (for simplicity, I changed the sharing content to a string):
#objc func shareButtonTapped()
{
let vc = UIActivityViewController(activityItems: ["www.apple.com"], applicationActivities: nil);
if let pop = vc.popoverPresentationController
{
pop.sourceView = someView;
pop.sourceRect = shareButton.frame;
}
self.present(vc, animated: true, completion: nil);
}
But when my iPhone was upgraded to iOS 15, the UIActivityViewController that is showed up was invisible. I attach an operation video:
enter link description here
Observe carefully, in fact, UIActivityViewController has a pop-up, but it has become almost transparent.
Then, I added a statement to deliberately set the background color of its view:
#objc func shareButtonTapped()
{
let vc = UIActivityViewController(activityItems: ["www.apple.com"], applicationActivities: nil);
vc.view.backgroundColor = UIColor.systemBackground;
if let pop = vc.popoverPresentationController
{
pop.sourceView = someView;
pop.sourceRect = shareButton.frame;
}
self.present(vc, animated: true, completion: nil);
}
The operation video is as follows:
enter link description here
This code is very simple and very standard. I don't know why this happens?
Hope someone can help. Thanks in advance!
In fact, I define the shareButton within a class derived from UICollectionViewCell:
class AssetPreviewCell: UICollectionViewCell, UIScrollViewDelegate, PHLiveViewDelegate, UINavigationControllerDelegate
{
//....
}
And the complete code is:
#objc func shareButtonTapped()
{
guard let svc = self.findViewController() else { return }
let vc = UIActivityViewController(activityItems: ["www.apple.com"], applicationActivities: nil);
if let pop = vc.popoverPresentationController
{
pop.sourceView = someView;
pop.sourceRect = shareButton.frame;
}
svc.present(vc, animated: true, completion: nil);
}
The func findViewController() is the method from enter link description here
Edit:
And I have an 'Albums' button next to the 'Share' button. When the 'Albums' button is tapped, I present another view controller for add current photo to albums or remove current photo from albums according user select or deselect. This view controller is presented quiet normally. The operation video is on enter link description here . So I think the problem is just from UIActivityViewController or something else.
I find the answer. The problem is caused because I overrided the viewDidLoad function of UIActivityViewController for some reason:
override open func viewDidLoad()
{
super.viewDidLoad();
a_var += 1;
}
This cause problem on iOS15 while works well on iOS14 or iOS13.
Now I override the viewDidAppear(_) instead and the problem dissappears:
override open func viewDidAppear(_ animated: Bool)
{
super.viewDidAppear(animated);
a_var += 1;
}
ps: #mczmma is another account of mine. This account is restricted to ask question. I don't know the reason.
What worked for me in my use case was just to change the sourceView from
let shareAll = [textView.text]
let activityViewController = UIActivityViewController(activityItems: shareAll, applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view
self.present(activityViewController, animated: true, completion: nil)
to
let shareAll = [textView.text]
let activityViewController = UIActivityViewController(activityItems: shareAll, applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = textView
self.present(activityViewController, animated: true, completion: nil)
So that on iPad the share shows above the textView, and it just works normally on iPhone.

SwiftUI Share Sheet Crashes iPad

I was following this tutorial https://jeevatamil.medium.com/how-to-create-share-sheet-uiactivityviewcontroller-in-swiftui-cef64b26f073
to add a simple share sheet to my swiftui app. It works properly on iPhones but crashes on iPad with this error
Terminating app due to uncaught exception 'NSGenericException', reason: 'UIPopoverPresentationController (<UIPopoverPresentationController: 0x107d95ee0>) should have a non-nil sourceView or barButtonItem set before the presentation occurs.'
Any way to get around this error? Not exactly sure what's happening here. Thanks!
You just need to set sourceView and sourceRect on the UIActivityViewController's popoverPresentationController. Here's a more complete and correct example than I've seen so far:
// Get a scene that's showing (iPad can have many instances of the same app, some in the background)
let activeScene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene
let rootViewController = (activeScene?.windows ?? []).first(where: { $0.isKeyWindow })?.rootViewController
// iPad stuff (fine to leave this in for all iOS devices, it will be effectively ignored when not needed)
activityVC.popoverPresentationController?.sourceView = rootViewController?.view
activityVC.popoverPresentationController?.sourceRect = .zero
rootViewController?.present(activityVC, animated: true, completion: nil)
Try this code to open Actionsheet in iPad.
if let vc = UIApplication.shared.windows.first?.rootViewController{
let activityVC = UIActivityViewController(activityItems: [urlShare], applicationActivities: nil)
if isIpad{
activityVC.popoverPresentationController?.sourceView = vc.view
activityVC.popoverPresentationController?.sourceRect = .zero
}
UIApplication.shared.windows.first?.rootViewController?.present(activityVC, animated: true, completion: nil)
}

UIActivityViewControllers across iOS8/iOS9 iPhone/iPad

My app features a share button which uses activityViewController on iPhones (iOS8+) and iPads (iOS9+). As iPads only use UIPopoverController on iOS8 and activityViewController on iOS9, I've implemented some conditions which direct each device to the appropriate controller. The problem is, iTunes Connect has shown crashes on iPads using UIPopoverController:
-[UIPopoverPresentationController presentationTransitionWillBegin]
Is the following code sound?
//if iOS9
if #available(iOS 9, *) {
self.presentViewController(activityViewController, animated: true, completion: nil)
}
//if iOS8
else {
//if iPad
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.Pad) {
let 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)
}
//if other
else {
self.presentViewController(activityViewController, animated: true, completion: nil)
}
}

What is the right code for the UIPopoverController

What is the right code for the PopoverController in iOS 9 have anywhere the right code for this code?
#IBAction func share(sender: UIBarButtonItem) {
let activityViewController = UIActivityViewController (
activityItems: [(webview.request?.URL!.absoluteString)! as NSString],
applicationActivities: nil)
if UIDevice.currentDevice().userInterfaceIdiom == .Pad {
//iPad
let activityPopover = UIPopoverController(contentViewController: activityViewController)
activityPopover.presentPopoverFromBarButtonItem(self.shareButton, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
}
else
{
//iPhone
presentViewController(activityViewController, animated: true, completion: nil)
}
}
}
Your code is ancient. You should throw all of that away. In iOS 8 and iOS 9, presented controllers adapt. UIActivityViewController is already a popover on iPad, automatically. Just present it and the right thing will happen.
Of course, you will have to supply it with a sourceView and sourceRect or a bar button item source. Otherwise, you'll crash on the iPad. But that's the case for any popover, so you can't be surprised about it.

IOS 8 iPad App Crashes When UIActivityViewController Is Called

When a UIActivityViewController is called on the iPhone in this app, it works perfectly, but when called on a iPad, the app crashes. Below is the code I used:
func shareButtonPress() {
//when the share button is pressed, default share phrase is added, cropped image of highscore is added
var sharingItems = [AnyObject]()
var shareButtonHighscore = NSUserDefaults.standardUserDefaults().objectForKey("highscore") as Int!
sharingItems.append("Just hit \(shareButtonHighscore)! Beat it! #Swath")
UIGraphicsBeginImageContextWithOptions(UIScreen.mainScreen().bounds.size, false, 0);
self.view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
var image:UIImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
sharingItems.append(image)
let activityViewController = UIActivityViewController(activityItems: sharingItems, applicationActivities: nil)
var barButtonItem: UIBarButtonItem! = UIBarButtonItem()
activityViewController.excludedActivityTypes = [UIActivityTypeCopyToPasteboard,UIActivityTypeAirDrop,UIActivityTypeAddToReadingList,UIActivityTypeAssignToContact,UIActivityTypePostToTencentWeibo,UIActivityTypePostToVimeo,UIActivityTypePrint,UIActivityTypeSaveToCameraRoll,UIActivityTypePostToWeibo]
self.presentViewController(activityViewController, animated: true, completion: nil)
}
As you can see, I'm programming in Swift, in the SpriteKit Framework, and I don't understand why the app is crashing.
I'm receiving this error:
Terminating app due to uncaught exception 'NSGenericException', reason: 'UIPopoverPresentationController (<_UIAlertControllerActionSheetRegularPresentationController: 0x7fc7a874bd90>) should have a non-nil sourceView or barButtonItem set before the presentation occurs.'
What can I do to fix this problem?
Before presenting the UIActivityViewController, add in this line of code:
activityViewController.popoverPresentationController?.sourceView = self.view
This way, the view controller knows in which frame of the GameViewController to appear in.
If you read the error it says how to fix it, you need to set the barButtonItem or sourceView from which to present the popover from, in your case:
func shareButtonPress(pressedButton: UIBarButtonItem) {
...
activityViewController.popoverPresentationController.barButtonItem = pressedButton
self.presentViewController(activityViewController, animated: true, completion: nil)
}
Swift 5:
Check if the device is iPhone or iPad and based on that add a sourceView and present the activityController
let activity = UIActivityViewController(activityItems: [self], applicationActivities: nil)
if UIDevice.current.userInterfaceIdiom == .phone {
UIApplication.topViewController?.present(activity, animated: true, completion: nil)
} else {
activity.popoverPresentationController?.sourceView = UIApplication.topViewController!.view
UIApplication.topViewController?.present(activity, animated: true, completion: nil)
}
There are two option, the action came from a UIBarButtonitem or UIButton that is a UIView.
func shareButtonPress() {
...
if let actv = activityViewController.popoverPresentationController {
actv.barButtonItem = someBarButton // if it is a UIBarButtonItem
// Or if it is a view you can get the view rect
actv.sourceView = someView
// actv.sourceRect = someView.frame // you can also specify the CGRect
}
self.presentViewController(activityViewController, animated: true, completion: nil)
}
You may have to add a sender to your function like func shareButtonPress(sender: UIBarButtonItem) or func shareButtonPress(sender: UIButton)
I added for Swift 3:
activityViewController.popoverPresentationController?.sourceView = self.view
fixed my issue.