17

我正在使用 iOS 8 新的自适应“Present As Popover”功能。我在 StoryBoard 中连接了一个简单的 segue 来进行演示。它在 iPhone 6 Plus 上效果很好,因为它将视图显示为弹出框,而在 iPhone 4s 上则显示为全屏视图(表格样式)。

问题是当显示为全屏视图时,我需要在视图中添加一个“完成”按钮,以便可以调用dismissViewControllerAnimated。当它显示为弹出框时,我不想显示“完成”按钮。

在此处输入图像描述

我尝试查看presentationController 和popoverPresentationController 的属性,但我找不到任何可以告诉我它是否实际显示为弹出框的信息。

NSLog( @"View loaded %lx", (long)self.presentationController.adaptivePresentationStyle );          // UIModalPresentationFullScreen
NSLog( @"View loaded %lx", (long)self.presentationController.presentationStyle );                  // UIModalPresentationPopover
NSLog( @"View loaded %lx", (long)self.popoverPresentationController.adaptivePresentationStyle );   // UIModalPresentationFullScreen
NSLog( @"View loaded %lx", (long)self.popoverPresentationController.presentationStyle );           // UIModalPresentationPopover

AdaptivePresentationStyle 总是返回 UIModalPresentationFullScreen,presentationStyle 总是返回 UIModalPresentationPopover

在查看 UITraitCollection 时,我确实发现了一个名为“_UITraitNameInteractionModel”的特征,它仅在实际显示为 Popover 时设置为 1。但是,Apple 不提供通过 popoverPresentationController 的 traitCollection 直接访问该 trait。

4

8 回答 8

13

我发现这样做的最好方法(最不臭)是使用UIPopoverPresentationControllerDelegate.

• 确保呈现的视图控制器设置为UIPopoverPresentationControllerDelegate用于UIPopoverPresentationController管理演示的。我正在使用情节提要,所以将其设置为prepareForSegue:

segue.destinationViewController.popoverPresentationController.delegate = presentedVC;

• 在呈现的视图控制器中创建一个属性来跟踪此状态:

@property (nonatomic, assign) BOOL amDisplayedInAPopover;

• 并添加以下委托方法(或添加到您现有的委托方法):

- (void)prepareForPopoverPresentation:(UIPopoverPresentationController *)popoverPresentationController
{
    // This method is only called if we are presented in a popover
    self.amDisplayedInAPopover = YES;
}

• 然后finally in viewWillAppear:-viewDidLoad:为时过早,委托prepare 方法在viewDidLoad:and之间调用viewWillAppear:

if (self.amDisplayedInAPopover) {
    // Hide the offending buttons in whatever manner you do so
    self.navigationItem.leftBarButtonItem = nil;
}

编辑:更简单的方法!

只需设置委托(确保您的presentedVC采用UIPopoverPresentationControllerDelegate):

segue.destinationViewController.popoverPresentationController.delegate = presentedVC;

并提供方法:

- (void)prepareForPopoverPresentation:(UIPopoverPresentationController *)popoverPresentationController
{
    // This method is only called if we are presented in a popover
    // Hide the offending buttons in whatever manner you do so
    self.navigationItem.leftBarButtonItem = nil;
}
于 2015-07-22T18:37:14.980 回答
10

我检查是否在布局视图后设置了 popoverPresentationController 的 arrowDirection。就我的目的而言,这已经足够好,并且涵盖了较小屏幕设备上的弹出框的情况。

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)

    if (popoverPresentationController?.arrowDirection != UIPopoverArrowDirection.Unknown) {
        // This view controller is running in a popover
        NSLog("I'm running in a Popover")
    }
}
于 2014-12-14T14:17:53.283 回答
3

怎么样

if (self.modalPresentationStyle == UIModalPresentationPopover)

它对我有用

于 2015-05-17T22:03:37.997 回答
3

我测试了这篇文章中提出的所有解决方案。抱歉,没有一个在所有情况下都能正常工作。例如,在 iPad 中,拆分视图显示样式可以在拖动拆分视图线时更改,因此我们需要为此提供特定通知。经过几个小时的研究,我在苹果样本(swift)中找到了解决方案: https ://developer.apple.com/library/ios/samplecode/AdaptivePhotos/Introduction/Intro.html#//apple_ref/doc/uid/TP40014636

这是 obj-c 中的相同解决方案。

首先在 prepareForSegue 函数中设置 popoverPresentationController 委托。它也可以在 MyViewController "init" 中设置,但不能在 "viewDidLoad" 中设置(因为在 viewDidLoad 之前首先调用 willPresentWithAdaptiveStyle)。

MyViewController *controller = [segue destinationViewController];
        controller.popoverPresentationController.delegate = (MyViewController *)controller;

现在 MyViewController 对象将在 iOS 每次更改演示样式时收到此通知,包括首次演示。这是在导航控制器中显示/隐藏“关闭”按钮的示例实现:

- (void)presentationController:(UIPresentationController *)presentationController
  willPresentWithAdaptiveStyle:(UIModalPresentationStyle)style
         transitionCoordinator:(nullable id<UIViewControllerTransitionCoordinator>)transitionCoordinator {
    if (style == UIModalPresentationNone) {
        // style set in storyboard not changed (popover), hide close button
        self.topViewController.navigationItem.leftBarButtonItem = nil;
    } else {
        // style changed by iOS (to fullscreen or page sheet), show close button
        UIBarButtonItem *closeButton =
            [[UIBarButtonItem alloc] initWithTitle:@"Close" style:UIBarButtonItemStylePlain target:self action:@selector(closeAction)];
        self.topViewController.navigationItem.leftBarButtonItem = closeButton;
    }
}

- (void)closeAction {
    [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
于 2016-05-12T07:54:35.980 回答
2

管理您的UIPresentationController视图控制器的方法是通过将其设置modalPresentationStyleUIModalPresentationPopover.

根据UIViewController 参考

呈现视图控制器

  • 呈现此视图控制器的视图控制器。(只读)

模态的PresentationStyle

  • UIModalPresentationPopover:在水平规则环境中,内容显示在弹出视图中的一种演示样式。背景内容变暗,在弹出框外点击会导致弹出框被关闭。如果您不希望点击关闭弹出框,您可以将一个或多个视图分配给关联的 UIPopoverPresentationController 对象的 passthroughViews 属性,您可以从 popoverPresentationController 属性中获取该属性。

因此,我们可以通过检查以下内容来确定您的视图控制器是在弹出框内还是模态显示horizontalSizeClass(我假设您的按钮是 a UIBarButtonItem

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    if (self.presentingViewController.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassRegular)
        self.navigationItem.leftBarButtonItem = nil; // remove the button
}

检查这个的最安全的地方是,viewWillAppear:否则presentingViewController可能是nil

于 2014-11-20T11:00:03.360 回答
2

实现这一点的官方方法是首先从您的视图控制器中删除完成按钮,其次,当适应紧凑型时,将您的视图控制器嵌入导航控制器中,将完成按钮添加为导航项:

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

func presentationController(controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
    let navigationController = UINavigationController(rootViewController: controller.presentedViewController)
    let btnDone = UIBarButtonItem(title: "Done", style: .Done, target: self, action: "dismiss")
    navigationController.topViewController.navigationItem.rightBarButtonItem = btnDone
    return navigationController
}

func dismiss() {
    self.dismissViewControllerAnimated(true, completion: nil)
}

完整教程

截图

于 2016-03-01T13:44:01.507 回答
2

适用于多任务处理的解决方案

将呈现控制器分配为弹出框的代表

...
controller.popoverPresentationController.delegate = controller;
[self presentViewController:controller animated:YES completion:nil];

然后,在控制器中,实现委托方法:

- (void)presentationController:(UIPresentationController *)presentationController willPresentWithAdaptiveStyle:(UIModalPresentationStyle)style transitionCoordinator:(id<UIViewControllerTransitionCoordinator>)transitionCoordinator
{
    if (style != UIModalPresentationNone)
    {
        // Exited popover mode
        self.navigationItem.leftBarButtonItem = button;
    }
}

- (void)prepareForPopoverPresentation:(UIPopoverPresentationController *)popoverPresentationController
{
    // Entered popover mode
    self.navigationItem.leftBarButtonItem = nil;
}
于 2016-10-06T23:33:27.237 回答
1

我棘手的解决方案,完美运行。

在. PopoverViewController_viewDidLoad

if (self.view.superview!.bounds != UIScreen.main.bounds) {
    print("This is a popover!")
}

这个想法很简单,除非它不是 Popover,否则 Popover 的视图大小永远不会等于设备屏幕大小。

于 2017-11-30T02:58:22.600 回答