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

6

31

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

Tuppeny answered 21/3, 2017 at 7:0 Comment(0)
F
12

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];
}
Fitz answered 7/4, 2017 at 14:24 Comment(0)
S
10

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

Spangle answered 3/2, 2021 at 20:52 Comment(5)
This worked for me. I was encountering a crash only on iPad (and not iPhone or iPod Touch). Actually I had to replace .sourceView with .barButtonItem and set it equal to the bar button item I had added to my navigationController.navigationItem.Symmetrical
You also have to set the sourceRect otherwise the popover won't displayShull
You need to set popOver.permittedArrowDirections = 0;, so that there are no arrows. If not set the popup didn't display for me (maybe because my source view is full screen). If I had arrows I needed to specify .sourceRect as well. I gave it the view.bounds but maybe because it was full screen view it didn't show the popup. I gave it something "imaginary" CGRectMake(view.origin.x, view.origin.y, 0, 0);Aspirant
This answer is incomplete. I tried this code and I did not see any popover.Rale
If anyone else is using swift, I had to use avc.popoverPresentationController?.sourceView = UIView() instead of avc.popoverPresentationController.sourceView = self.view;Monetmoneta
L
6

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;
Level answered 27/7, 2017 at 9:37 Comment(1)
You inverted sourceView and sourceRect assignments in the last two lines of your sample.Aerography
B
2

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];
}
Brazee answered 21/3, 2017 at 7:21 Comment(1)
UIPopoverController is deprecated from iOS 9 onwardsTuppeny
S
2

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)
    }
}
Sexist answered 16/5, 2019 at 7:53 Comment(1)
I once encountered a situation。If there is not enough memory, it will be automatically reclaimedSexist
N
0

my two cents for activity (and in swift 5..)

..

 let activityViewController = UIActivityViewController(
                activityItems: shareTextAndImg, applicationActivities: [])
 if UIDevice.current.userInterfaceIdiom == .pad {
   activityViewController.popoverPresentationController?.barButtonItem = btn
  }

present(activityViewController, animated: true)
Nog answered 20/2, 2022 at 10:21 Comment(2)
What's in the btn variable?Rettke
btw is the button in UI where present it.Nog

© 2022 - 2024 — McMap. All rights reserved.