1
votes

I have an issue while displaying a UIAlertController of type UIActionSheet in iPad. I know iPads need more information in order to display the popover, but I struggle with some strange issues.

I'm checking iOS 13 compatibility issues with my app and it appears the old trick like getting the view with performSelector on a UIBarButtonItem doesn't work anymore.

Way 1

So I do this.

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
    UIView* internalView = (UIView*) [weakSelf.navigationItem.rightBarButtonItem performSelector:@selector(view)];

    menuController.popoverPresentationController.sourceView = weakSelf.navigationController.navigationBar;
    menuController.popoverPresentationController.sourceRect = internalView.frame;
    menuController.popoverPresentationController.canOverlapSourceViewRect = YES;
}

internalView seems to be nil instead of getting a UIView in older versions of iOS. The popover displays in top left corner of the screen instead of near the navigation bar button item.

Way 2

So I try another way to do this, the "proper way" in fact, I guess:

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
    menuController.popoverPresentationController.barButtonItem = weakSelf.navigationItem.rightBarButtonItem;
}

When I try with this code, the app crashes with the message that explains I have to set sourceView/sourceRect/barButtonItem. But I tell it to do it.

Terminating app due to uncaught exception 'NSGenericException', reason: 'Your application has presented a UIAlertController () of style UIAlertControllerStyleActionSheet from UISmartNavigationController (). The modalPresentationStyle of a UIAlertController with this style is UIModalPresentationPopover. You must provide location information for this popover through the alert controller's popoverPresentationController. You must provide either a sourceView and sourceRect or a barButtonItem. If this information is not known when you present the alert controller, you may provide it in the UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.'

I try with an UIPopoverPresentationControllerDelegate as well but I get same results.

I checked and rechecked, weakSelf.navigationItem.rightBarButtonItem is not nil and set to the right bar button item. I guess I'm missing something huge, but what?

1
"Way 1" was never correct. "Way 2" is correct but you need to call it at the right time and place. And there is no need to check for iPad. The same code is valid on all devices. You should update your question showing more code for "Way 2". Show all relevant code for creating, setting up, and displaying the action sheet.rmaddy
Did you find a solution to the problem? I have the same issue since upgrading to iOS 13.Herbert Bay

1 Answers

0
votes

The Apple documentation refers to this page. https://developer.apple.com/documentation/uikit/windows_and_screens/displaying_transient_content_in_a_popover However, the sample code references an object optionsControl which has never been defined. I can get several variations to build, deploy and run, but all crash with a similar error like: Terminating app due to uncaught exception 'NSGenericException', reason: 'UIPopoverPresentationController () should have a non-nil sourceView or barButtonItem set before the presentation occurs.' Finally after 3 days of reading, I discovered this page UIActivityViewController crashing on iOS 8 iPads which helped me create a working version below. Which is surprisingly much simpler than other things I have tried. The key is setting the sourceView of the controller to the parent view which for me was object.view because object is reference to the parent view controller passed in as the sender.

static func transmitAirDrop(object: ProjectOrganizerMasterViewController) {
    NSLog("UTVCU.transmitAirDrop")
    if (StatusReportSettings.supportsAirdrop() == false) {
        self.sendMultipleXMLEmail(object: object)
        return;
    }
    let myDelegate : StatusReportAppDelegate = UITableViewController.sharedDelegate()
    let documentsDirectory : String = UITableViewController.persistProjectFiles(managedObjectContext: object.managedObjectContext, delegate: myDelegate as! ProjectOrganizerAppDelegate)

    let backupZipFileName : String = UITableViewController.backupExportZipFileName(StatusReportSettings.appSpecificFileName())!

    let password1 : String = object.passwordPromptController?.textFields?.first?.text ?? ""
    let backupZipFilePath : String = StatusReportMasterViewController.createPasswordProtectedExportZipFile(password1: password1, documentsDirectory: documentsDirectory)

    let url : URL = URL.init(fileURLWithPath: backupZipFilePath)
    NSLog("transmitAirDrop() url ->%@", url.path);



    let objectsToShare : NSArray = [url]

    NSLog("UTVCU.transmitAirDrop presentViewController calling");

    let controller = UIActivityViewController(activityItems: [url], applicationActivities: nil)
    controller.excludedActivityTypes = [.postToFacebook,
        .postToTwitter,
        .print,
        .copyToPasteboard,
        .assignToContact,
        .saveToCameraRoll]

    controller.popoverPresentationController?.sourceView = object.view


    object.present(controller, animated: true, completion: nil)


    NSLog("UTVCU.transmitAirDrop presentViewController returned");

}