iOS 8 presentationController determine if really is popover

后端 未结 8 934
暖寄归人
暖寄归人 2020-12-15 20:33

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 P

相关标签:
8条回答
  • 2020-12-15 21:09

    How about

    if (self.modalPresentationStyle == UIModalPresentationPopover)
    

    It's working for me

    0 讨论(0)
  • 2020-12-15 21:13

    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.

    0 讨论(0)
  • 2020-12-15 21:19

    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];
    }
    
    0 讨论(0)
  • 2020-12-15 21:24

    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;
    }
    
    0 讨论(0)
  • 2020-12-15 21:26

    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.

    0 讨论(0)
  • 2020-12-15 21:26

    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

    0 讨论(0)
提交回复
热议问题