iOS 9上,UIPopoverPresentationController在呈现之前应该设置非空的sourceView或barButtonItem。

31

我正在尝试使用自定义的UIPopoverPresentationController类来显示一个弹出窗口。但是它会崩溃并显示错误(<UIPopoverPresentationController: 0x7a772950>) should have a non-nil sourceView or barButtonItem set before the presentation 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];
}

以下是我自定义的UIPopoverPresentationController样例

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;
}
我没有一个barbutton,但我正在设置sourceView。这里做错了什么吗?感谢您的帮助。
6个回答

12

您也可以像这样创建一个弹出式演示控制器,它可能会起作用:

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

10
如果sourceView为空,只需添加一个验证。
UIActivityViewController * avc = [[UIActivityViewController alloc] initWithActivityItems:shareItems applicationActivities:nil];
if(avc.popoverPresentationController){
    avc.popoverPresentationController.sourceView = self.view;
}
[self presentViewController:avc animated:YES completion:nil];

请查看此文章


3
这对我有用。我只在iPad上遇到了崩溃问题(而不是iPhone或iPod Touch)。实际上,我不得不将.sourceView替换为.barButtonItem,并将其设置为我添加到navigationController.navigationItem的工具栏按钮项。 - Chris Allinson
你还需要设置 sourceRect,否则弹出窗口将无法显示。 - kbunarjo
你需要设置 popOver.permittedArrowDirections = 0;,这样就没有箭头了。如果不设置,弹出窗口可能无法显示(可能是因为我的源视图是全屏的)。如果我有箭头,我还需要指定 .sourceRect。我给它了 view.bounds,但可能因为它是全屏视图,所以它没有显示弹出窗口。我给了它一个“虚构”的 CGRectMake(view.origin.x, view.origin.y, 0, 0); - h3dkandi
这个答案不完整。我尝试了这段代码,但是没有看到任何弹出窗口。 - Rohit Singh

6
您正在创建UIPopoverPresentationController的子类,但是苹果公司建议直接使用它们。一旦您呈现了一个UIViewController,就会自动创建一个UIPopoverPresentationController,您应该根据需要对其进行修改。
您创建了一个名为myPopoverController的实例,但是在您呈现contentViewController之后,苹果会创建另一个实例:
[self presentViewController:contentViewController animated: YES completion: nil];

这个新的UIPopoverPresentationController缺少sourceView,会抛出异常。

请尝试使用以下代码:

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;

3
在您的示例中,您颠倒了最后两行中的sourceView和sourceRect赋值。 - Martin-Gilles Lavoie

2
我认为最好的方法是覆盖现有的方法。"Original Answer"翻译成"最初的回答"。
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)
    }
}

我曾经遇到过这样的情况。如果内存不足,它会被自动回收。 - kkklc

2
也许下面的代码可以帮助您:
在iPad中,视图控制器将作为弹出窗口显示,使用新的UIPopoverPresentationController,需要指定一个锚点来呈现弹出窗口,可以使用以下三个属性之一:
1. barButtonItem 2. sourceView 3. sourceRect
请按照以下步骤操作:
//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];
}

1
UIPopoverController从iOS 9开始已被弃用。 - Francis F

0

关于活动(在Swift 5中的)我的两分意见:

..

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

present(activityViewController, animated: true)

2
btn 变量里面是什么? - soger
顺便提一下,在 UI 中,btw 是指按钮的位置。 - ingconti

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接