UIPopoverPresentationController should have a non-nil sourceView or barButtonItem set before the presentation occurs on iOS 9

21,652

Solution 1

You can create a popover presentation controller like this also and it may work

- (IBAction)showPopup:(UIButton *)sender {

ViewController *contentViewController = [[ViewController alloc] init];
    contentViewController.preferredContentSize = CGSizeMake(200, 200);
    contentViewController.modalPresentationStyle = UIModalPresentationPopover;

UIPopoverPresentationController *popoverpresentationController = contentViewController.popoverPresentationController;
    popoverpresentationController.delegate = self;
    popoverpresentationController.permittedArrowDirections = UIPopoverArrowDirectionUp;
    popoverpresentationController.sourceRect = sender.bounds;
    popoverpresentationController.sourceView = sender;
    [self presentViewController:contentViewController animated: YES completion: nil];
}

Solution 2

If sourceView is null, just add a validation

UIActivityViewController * avc = [[UIActivityViewController alloc] initWithActivityItems:shareItems applicationActivities:nil];
if(avc.popoverPresentationController){
    avc.popoverPresentationController.sourceView = self.view;
}
[self presentViewController:avc animated:YES completion:nil];

See this post

Solution 3

You are subclassing the UIPopoverPresentationController but Apple recommends to use them as they are. Once you present a UIViewController, a UIPopoverPresentationController will be automatically created and you are supposed to modify it for your needs.

You create an myPopoverController instance but Apple creates another when you present your contentViewController right after:

[self presentViewController:contentViewController animated: YES completion: nil];

This new UIPopoverPresentationController lacks the sourceView and throws an exception.

Try the code below instead:

ViewController *contentViewController = [[ViewController alloc] init];

// Present the view controller using the popover style.
contentViewController.modalPresentationStyle = UIModalPresentationPopover;
[self presentViewController:contentViewController 
                   animated:YES 
                 completion:nil];

// Get the popover presentation controller and configure it.
UIPopoverPresentationController *presentationController =[contentViewController popoverPresentationController];
presentationController.permittedArrowDirections = UIPopoverArrowDirectionUp;
presentationController.sourceView = self.view;
presentationController.sourceRect = sender.frame;

Solution 4

May be below code could help:

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

  1. barButtonItem
  2. sourceView
  3. sourceRect

Do as follows:

//for iPhone
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {

    [self presentViewController:controller animated:YES completion:nil];

}
//for iPad
else {
    // Change Rect as required
    ViewController *contentViewController = [[ViewController alloc] init];

    contentViewController.preferredContentSize = CGSizeMake(200, 200);
    contentViewController.modalPresentationStyle = UIModalPresentationPopover;
    [self presentViewController:contentViewController animated:YES completion:nil];
}

Solution 5

The Best way I think is override present method

extension XXXBaseViewController: UIPopoverPresentationControllerDelegate {

    override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
        if let popController = viewControllerToPresent.popoverPresentationController,
            popController.sourceView == nil{
            return
        }
        super.present(viewControllerToPresent, animated: flag, completion: completion)
    }
}
Share:
21,652
Francis F
Author by

Francis F

I am an iOS developer with over six years’ experience writing highly readable, clean, maintainable source code. I make it my goal to create software with the user in mind, creating applications with a usable and intuitive user interface experience and I also stand for quality. I am constantly striving to learn new technologies and look to ways to better myself in this rapidly changing industry.

Updated on July 09, 2022

Comments

  • Francis F
    Francis F almost 2 years

    I'm trying to show a popup using a custom UIPopoverPresentationController class. But it crashes with the error(<UIPopoverPresentationController: 0x7a772950>) should have a non-nil sourceView or barButtonItem set before the presentation occurs. Below is my button click code where the crash occurs.

    - (IBAction)showPopup:(UIButton *)sender {
    ViewController *contentViewController = [[ViewController alloc] init];
    
        contentViewController.preferredContentSize = CGSizeMake(200, 200);
        contentViewController.modalPresentationStyle = UIModalPresentationPopover;
        myPopoverController *popOver = [[myPopoverController alloc]initWithPresentedViewController:contentViewController presentingViewController:self andTintColor:[UIColor lightGrayColor]];
    
        popOver.delegate = self;
        popOver.permittedArrowDirections = UIPopoverArrowDirectionUp;
        popOver.sourceRect = sender.frame;
        popOver.sourceView = self.view;
        [self presentViewController:contentViewController animated: YES completion: nil];
    }
    

    Below is a sample of how my custom UIPopoverPresentationController looks like

    myPopoverController.h file
    
    @interface myPopoverController : UIPopoverPresentationController
    
    @property (readonly) UIColor *tintColor;
    
    
    -(instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(UIViewController *)presentingViewController andTintColor:(UIColor *)aTintColor;
    
    @end
    
    
    myPopoverController.m file
    
    //Some code for UIPopoverBackgroundView
    
    -(instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(UIViewController *)presentingViewController
    {
    
        self = [self initWithPresentedViewController:presentedViewController presentingViewController:presentingViewController andTintColor: [UIColor redColor]];
    
        return self;
    }
    
    
    -(instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(UIViewController *)presentingViewController andTintColor:(UIColor *)aTintColor
    {
    
        self = [super initWithPresentedViewController:presentedViewController presentingViewController:presentingViewController];
    
        if (!self) {
            return nil;
        }
    
        [super setPopoverBackgroundViewClass: [myPopoverControllerBackgroundView class]];
        tintColor = aTintColor;
    
    
        return self;
    }
    

    I don't have a barbutton but I'm setting the sourceView. Am I doing something wrong here? Appreciate your help