iOS 8 presentationController determine if really is popover

梦想与她 提交于 2019-12-29 05:13:11

问题


I'm using the new adaptive "Present As Popover" capability of iOS 8. I wired up a simple segue in the StoryBoard to do the presentation. It works great on an iPhone 6 Plus as it presents the view as a popover and on an iPhone 4s it shows as a full screen view (sheet style).

The problem is when shown as a full screen view, I need to add a "Done" button to the view so dismissViewControllerAnimated can be called. And I don't want to show the "done" button when it's shown as a popover.

I tried looking at the properties of both presentationController and popoverPresentationController, and I can find nothing that tells me if it is actually being shown as a popover.

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 always returns UIModalPresentationFullScreen and presentationStyle always returns UIModalPresentationPopover

When looking at the UITraitCollection I did find a trait called "_UITraitNameInteractionModel" which was only set to 1 when it was actually displayed as a Popover. However, Apple doesn't provide direct access to that trait through the traitCollection of popoverPresentationController.


回答1:


The best way (least smelly) I've found to do this is to use the UIPopoverPresentationControllerDelegate.

• Ensure the presented view controller is set as the UIPopoverPresentationControllerDelegate on the UIPopoverPresentationController being used to manage the presentation. I'm using a Storyboard so set this in prepareForSegue:

segue.destinationViewController.popoverPresentationController.delegate = presentedVC;

• Create a property in the presented view controller to keep track of this state:

@property (nonatomic, assign) BOOL amDisplayedInAPopover;

• And add the following delegate method (or add to your existing delegate method):

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

• And then finally in viewWillAppear: - viewDidLoad: is too early, the delegate prepare method is called between viewDidLoad: and viewWillAppear:

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

Edit: Simpler method!

Just set the delegate (making sure your presentedVC adopts the UIPopoverPresentationControllerDelegate):

segue.destinationViewController.popoverPresentationController.delegate = presentedVC;

And supply the method:

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



回答2:


I check to see if the popoverPresentationController's arrowDirection is set after the view is laid out. For my purposes, this works well enough and covers the case of popovers on smaller screened devices.

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")
    }
}



回答3:


How about

if (self.modalPresentationStyle == UIModalPresentationPopover)

It's working for me




回答4:


I tested all solutions presented in this post. Sorry, none works correctly in all cases. For example in iPad split view presentation style can change while dragging split view line, so we need specific notification for that. After few hours of researches i found solution in apple sample (swift): https://developer.apple.com/library/ios/samplecode/AdaptivePhotos/Introduction/Intro.html#//apple_ref/doc/uid/TP40014636

Here is the same solution in obj-c.

First in prepareForSegue function set the popoverPresentationController delegate. It can be also set in MyViewController "init", but not in "viewDidLoad" (because first willPresentWithAdaptiveStyle is called before viewDidLoad).

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

Now MyViewController object will receive this notification every time iOS changes presentation style, including first presenting. Here is example implementation which shows/hides "Close" button in navigationController:

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



回答5:


The UIPresentationController which manages your view controller is presenting it by setting the modalPresentationStyle to UIModalPresentationPopover.

As per UIViewController reference:

presentingViewController

  • The view controller that presented this view controller. (read-only)

modalPresentationStyle

  • UIModalPresentationPopover: In a horizontally regular environment, a presentation style where the content is displayed in a popover view. The background content is dimmed and taps outside the popover cause the popover to be dismissed. If you do not want taps to dismiss the popover, you can assign one or more views to the passthroughViews property of the associated UIPopoverPresentationController object, which you can get from the popoverPresentationController property.

We can therefore determine whether your view controller is inside a popover or presented modally by checking the horizontalSizeClass as follows (I assumed your button is a UIBarButtonItem)

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

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

The safest place to check this is in viewWillAppear: as otherwise the presentingViewController may be nil.




回答6:


The official way to implement this is first remove the Done button from your view controller and second, when adapting to compact embed your view controller in a navigation controller, adding the done button as a navigation item:

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)
}

Full Tutorial




回答7:


Solution that works with multitasking

Assign the presenting controller as the popover's delegate

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

Then, in the controller, implement the delegate methods:

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



回答8:


My tricky solution, works perfectly.

In the PopoverViewController's viewDidLoad.

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

The idea is simple, A Popover's view size is never equal to the device screen size unless it's not a Popover.



来源:https://stackoverflow.com/questions/26687017/ios-8-presentationcontroller-determine-if-really-is-popover

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!