iOS 8 iPhone上的UIPopoverPresentationController

75
有人知道在iPhone上是否可以使用UIPopoverPresentationController来呈现弹出窗口吗?想知道苹果在iOS 8中是否添加了此功能,以尝试为iPad和iPhone创建更统一的表示控制器。
不确定是否可以从Beta版提问/回答问题。如果是这种情况,我将删除它。
9个回答

84

您可以通过使用UIPopoverPresentationController.delegate提供的adaptivePresentationStyleForPresentationController:方法来覆盖默认的自适应行为(在紧凑的水平环境,即iPhone中使用UIModalPresentationFullScreen),

UIPresentationController使用此方法来请求要使用的新呈现样式,在这种情况下,简单地返回UIModalPresentationNone将导致UIPopoverPresentationController作为弹出窗口而不是全屏进行渲染。

以下是使用故事板中从UIBarButtonItem到"以模态方式呈现"UIViewController的转场设置的示例弹出窗口:

class SomeViewController: UIViewController, UIPopoverPresentationControllerDelegate {

    // override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) { // swift < 3.0
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "PopoverSegue" {
            if let controller = segue.destinationViewController as? UIViewController {
                controller.popoverPresentationController.delegate = self
                controller.preferredContentSize = CGSize(width: 320, height: 186)                
            }
        }
    }

    // MARK: UIPopoverPresentationControllerDelegate

    //func adaptivePresentationStyleForPresentationController(controller: UIPresentationController!) -> UIModalPresentationStyle { // swift < 3.0
    func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
        // Return no adaptive presentation style, use default presentation behaviour
        return .None
    }
}

这个技巧在WWDC 2014第214节“iOS8中的视图控制器进阶”(36:30)中提到。


2
这在 iPhone 的 GM 版本中有 bug。如果你尝试在模拟器处于竖屏状态下进行 present 操作,它总是全屏显示。如果你将其旋转到横屏状态,它会变成一个弹出窗口。如果你再次将其旋转回竖屏状态,它仍然是一个弹出窗口。 - jjxtra
17
解决方案是在调用presentViewController之前设置弹出视图。这恰好与苹果的示例相反,他们明确告诉你在调用presentViewController之后设置弹出视图。 - jjxtra
4
无法读懂 Swift 代码的人可以参考以下 Objective-C 代码:http://stackoverflow.com/a/28143620/1228075 - Armin
@Daniel 确实。在展示之前设置它更有意义,而且苹果在发布之前可能已经改变了这一点。 - jjxtra
还需要为UIPopoverPresentationController设置sourceView或barButtonItem。 - banxi1988
显示剩余5条评论

77
如果有人想要仅使用代码展示一个弹出框,可以使用以下方法。 Objective-C 声明一个 UIPopoverPresentationController 属性:
@property(nonatomic,retain)UIPopoverPresentationController *dateTimePopover8;

使用以下方法从UIButton中显示弹出窗口:

- (IBAction)btnSelectDatePressed:(id)sender
{
    UINavigationController *destNav = [[UINavigationController alloc] initWithRootViewController:dateVC];/*Here dateVC is controller you want to show in popover*/
    dateVC.preferredContentSize = CGSizeMake(280,200);
    destNav.modalPresentationStyle = UIModalPresentationPopover;
    _dateTimePopover8 = destNav.popoverPresentationController;
    _dateTimePopover8.delegate = self;
    _dateTimePopover8.sourceView = self.view;
    _dateTimePopover8.sourceRect = sender.frame;
    destNav.navigationBarHidden = YES;
    [self presentViewController:destNav animated:YES completion:nil];
}

使用下面的方法从UIBarButtonItem中弹出气泡窗口:

- (IBAction)btnSelectDatePressed:(id)sender
{
    UINavigationController *destNav = [[UINavigationController alloc] initWithRootViewController:dateVC];/*Here dateVC is controller you want to show in popover*/
    dateVC.preferredContentSize = CGSizeMake(280,200);
    destNav.modalPresentationStyle = UIModalPresentationPopover;
    _dateTimePopover8 = destNav.popoverPresentationController;
    _dateTimePopover8.delegate = self;
    _dateTimePopover8.sourceView = self.view;
     CGRect frame = [[sender valueForKey:@"view"] frame];
    frame.origin.y = frame.origin.y+20;
    _dateTimePopover8.sourceRect = frame;
    destNav.navigationBarHidden = YES;
    [self presentViewController:destNav animated:YES completion:nil];
}

在您的视图控制器中也要实现此委托方法:
- (UIModalPresentationStyle) adaptivePresentationStyleForPresentationController: (UIPresentationController * ) controller {
    return UIModalPresentationNone;
}

要关闭此弹出窗口,只需关闭视图控制器。以下是关闭视图控制器的代码:

dismissViewControllerAnimated (true, completion: nil)

-(void)hideIOS8PopOver
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

SWIFT

使用以下方法从UIButon显示气泡:

func filterBooks(sender: UIButon)
    {
        let filterVC =  FilterDistanceViewController(nibName: "FilterDistanceViewController", bundle: nil)
        var filterDistanceViewController = UINavigationController(rootViewController: filterVC)
        filterDistanceViewController.preferredContentSize = CGSizeMake(300, 205)
        let popoverPresentationViewController = filterDistanceViewController.popoverPresentationController
        popoverPresentationViewController?.permittedArrowDirections = .Any
        popoverPresentationViewController?.delegate = self
        popoverPresentationController?.barButtonItem = self.navigationItem.rightBarButtonItem
        popoverPresentationViewController!.sourceView = self.view;
        popoverPresentationViewController!.sourceRect = sender.frame

        filterDistanceViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
        filterDistanceViewController.navigationBarHidden = true
        self.presentViewController(filterDistanceViewController, animated: true, completion: nil)
    }

使用以下方法从UIBarButtonItem中呈现气泡窗口:

func filterBooks(sender: UIBarButtonItem)
    {
        let filterVC =  FilterDistanceViewController(nibName: "FilterDistanceViewController", bundle: nil)
        var filterDistanceViewController = UINavigationController(rootViewController: filterVC)
        filterDistanceViewController.preferredContentSize = CGSizeMake(300, 205)
        let popoverPresentationViewController = filterDistanceViewController.popoverPresentationController
        popoverPresentationViewController?.permittedArrowDirections = .Any
        popoverPresentationViewController?.delegate = self
        popoverPresentationController?.barButtonItem = self.navigationItem.rightBarButtonItem
        popoverPresentationViewController!.sourceView = self.view;
        var frame:CGRect = sender.valueForKey("view")!.frame
        frame.origin.y = frame.origin.y+20
        popoverPresentationViewController!.sourceRect = frame

        filterDistanceViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
        filterDistanceViewController.navigationBarHidden = true
        self.presentViewController(filterDistanceViewController, animated: true, completion: nil)
    }

在您的视图控制器中也实现此委托方法:

func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle{
        return .None
    }

请确保在.h/.m/.swift文件中添加UIPopoverPresentationControllerDelegate代理。

1
我认为不需要声明UIPopoverPresentationController。当我通过目标视图控制器简单访问并设置其属性时,它对我有效。我正在使用Swift和Xcode 6.2。 - Daniel
1
destNav.popoverPresentationController 是空的。 - pronebird
如果您遇到了这个问题,请前往文档并点击反馈按钮,建议他们更正代码清单,以帮助其他人解决问题。 - hwaxxer
哦,苹果自己的文档提到了你写的所有内容,只是没有包括adaptivePresentationStyleForPresentationController:方法。在我加入这个方法之前,什么都不起作用。谢谢! - RyJ
1
不适用于我的情况:dateVC是在没有Storyboard的情况下初始化的。 - LHA
显示剩余3条评论

12

问题: iPhone上的弹出窗口全屏显示,不遵守preferredContentSize值。

解决方案: 与苹果在UIPopoverPresentationController类参考中建议的相反,在获取到弹出窗口表示控制器并进行配置之后,再呈现视图控制器。

// Get the popover presentation controller and configure it.
//...

// Present the view controller using the popover style.
[self presentViewController:myPopoverViewController animated: YES completion: nil]; 

同样的问题,在实现了委托和一切之后不够,配置后呈现才能在iPhone 6S iOS 10.1上解决问题。 - dvkch
在 presentViewController 之前设置委托非常重要。 - BugaBuga

4

请确保按照以下方式实现UIAdaptivePresentationControllerDelegate

- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
    return UIModalPresentationNone;
}

如果您不想要全屏弹出窗口


2
我找到了一些解决方法。
在 Xcode6.1 中,可以使用 presentationController.delegate 替代 popoverPresentationController.delegate
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier compare:@"showPopOver"] == NSOrderedSame) {
        UINavigationController * nvc = segue.destinationViewController;
        UIPresentationController * pc = nvc.presentationController;
        pc.delegate = self;
    }
}

#pragma mark == UIPopoverPresentationControllerDelegate ==
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller
{
    return UIModalPresentationNone;
}

在WWDC 2014年的“iOS8中的视图控制器进展”中,以下代码可以在iPhone上显示弹出窗口。
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{

    UINavigationController * nvc = segue.destinationViewController;
    UIPopoverPresentationController * pvc = nvc.popoverPresentationController;
    pvc.delegate = self;
}

#pragma mark == UIPopoverPresentationControllerDelegate ==
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller
{
    return UIModalPresentationNone;
}

但在Xcode 6.1上,这些代码显示了全屏呈现...(nvc.popoverPresentationController为nil)

我怀疑这可能是苹果的一个bug。


1
在iOS 8.3及以上版本中,使用以下语法在UIPopoverPresentationControllerDelegate协议中覆盖您的弹出窗口的UIModalPresentationStyle
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
    return .none
}

1
您可以像这样扩展UIPopoverPresentationControllerDelegate:
protocol PopoverPresentationSourceView {}
extension UIBarButtonItem : PopoverPresentationSourceView {}
extension UIView : PopoverPresentationSourceView {}

extension UIPopoverPresentationControllerDelegate where Self : UIViewController {
   func present(popover: UIViewController, 
        from sourceView: PopoverPresentationSourceView, 
        size: CGSize, arrowDirection: UIPopoverArrowDirection) {

      popover.modalPresentationStyle = .popover
      popover.preferredContentSize = size
      let popoverController = popover.popoverPresentationController
      popoverController?.delegate = self
      if let aView = sourceView as? UIView {
          popoverController?.sourceView = aView
          popoverController?.sourceRect = CGRect(x: aView.bounds.midX, y: aView.bounds.midY, width: 0, height: 0)
      } else if let barButtonItem = sourceView as? UIBarButtonItem {
          popoverController?.barButtonItem = barButtonItem
      }
      popoverController?.permittedArrowDirections = arrowDirection
      present(popover, animated: true, completion: nil)
   }
}

现在,您可以从任何实现 UIPopoverPresentationControllerDelegate 的视图控制器中调用 present(popover: from: size: arrowDirection: ) 方法。例如:

class YourViewController : UIViewController {
    @IBAction func someButtonPressed(_ sender: UIButton) {
        let popover = SomeViewController()
        present(popover: popover, from: sender, size: CGSize(width: 280, height: 400), arrowDirection: .right)
    }
}

extension YourViewController : UIPopoverPresentationControllerDelegate {
    func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
        return .none
    }
}

0
在UIAdaptivePresentationControllerDelegate中,您必须使用此方法:
func adaptivePresentationStyle(for: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle

用这个代替:

func adaptivePresentationStyle(for: UIPresentationController) -> UIModalPresentationStyle

0
在你的WEBVIEW类中添加这两个方法。并添加。
-(void) prepareForSegue: (UIStoryboardSegue * ) segue sender: (id) sender {
    // Assuming you've hooked this all up in a Storyboard with a popover presentation style
    if ([segue.identifier isEqualToString: @"showPopover"]) {
        UINavigationController * destNav = segue.destinationViewController;
        pop = destNav.viewControllers.firstObject;
        // This is the important part
        UIPopoverPresentationController * popPC = destNav.popoverPresentationController;
        popPC.delegate = self;
    }
}

- (UIModalPresentationStyle) adaptivePresentationStyleForPresentationController: (UIPresentationController * ) controller {
    return UIModalPresentationNone;
}

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