How can I fix the "UIPopoverController is deprecated" warning?

IosObjective CUipopovercontrollerDeprecated

Ios Problem Overview


I am using this code:

mediaLibraryPopover = [[UIPopoverController alloc] 
                        initWithContentViewController:avc];
[self.mediaLibraryPopover presentPopoverFromRect:[theButton bounds] 
                          inView:theButton 
                          permittedArrowDirections:UIPopoverArrowDirectionAny 
                          animated:YES];

And I am getting this warning in Xcode 7:

> UIPopoverController is deprecated, first deprecated in iOS 9.0 - UIPopoverController is deprecated. Popovers are now implemented as UIViewController presentations. Use a modal presentation style of UIModalPresentationPopover and UIPopoverPresentationController.

Ios Solutions


Solution 1 - Ios

You no longer need UIPopoverController for presenting a view controller. Instead you can set the modalPresentationStyle of view controller to UIModalPresentationPopover.

You can use the following code for that:

avc.modalPresentationStyle = UIModalPresentationPopover;
avc.popoverPresentationController.sourceView = theButton;
[self presentViewController:avc animated:YES completion:nil];

> 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. > > In a horizontally compact environment, this option behaves the same as > UIModalPresentationFullScreen. > > Available in iOS 8.0 and later.

Reference UIModalPresentationStyle Reference


You need to set either sourceView or barButtonItem property, else it will crash with the following message:

> *** Terminating app due to uncaught exception 'NSGenericException', reason: 'UIPopoverPresentationController (***) should have a non-nil > sourceView or barButtonItem set before the presentation occurs.'

For anchoring the popover arrow correctly, you need to specify the sourceRect property also.

avc.modalPresentationStyle                   = UIModalPresentationPopover;
avc.popoverPresentationController.sourceView = self.view;
avc.popoverPresentationController.sourceRect = theButton.frame;
[self presentViewController:avc animated:YES completion:nil];

Refer sourceView and sourceRect for more details.

Solution 2 - Ios

Apple has the official way to present and configure popovers for iOS8 here: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIPopoverPresentationController_class/index.html

While similar to @MidhunMP's answer, it's worth noting the paragraph:

> Configuring the popover presentation controller after calling > presentViewController:animated:completion: might seem > counter-intuitive but UIKit does not create a presentation controller > until after you initiate a presentation. In addition, UIKit must wait > until the next update cycle to display new content onscreen anyway. > That delay gives you time to configure the presentation controller for > your popover.

Configuration and responding to events can also be done via a delegate if you wanted (https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIPopoverPresentationControllerDelegate_protocol/index.html).

An example, setting aside the use of the delegate:

// Present the controller using the popover style.
controller.modalPresentationStyle = UIModalPresentationPopover;
[self presentViewController:controller animated:YES completion:nil];

// Popover presentation controller was created when presenting; now  configure it.
UIPopoverPresentationController *presentationController =
        [controller popoverPresentationController];
presentationController.permittedArrowDirections = UIPopoverArrowDirectionLeft;
presentationController.sourceView = containerFrameOfReferenceView;
// arrow points out of the rect specified here
presentationController.sourceRect = childOfContainerView.frame;

But you'll also want to dismiss this. To do so without using a delegate, your presenting controller can just call:

[self dismissViewControllerAnimated:YES completion:nil];

But what if I rotate my device, and the popover doesn't point to the right area? Your presenting controller can handle it:

// Respond to rotations or split screen changes
- (void)viewWillTransitionToSize:(CGSize)size
       withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
    [coordinator animateAlongsideTransition:nil 
                                 completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
        // Fix up popover placement if necessary, after the transition.
        if (self.presentedViewController) {
            UIPopoverPresentationController *presentationController =
                    [self.presentedViewController popoverPresentationController];
            presentationController.sourceView = containerFrameOfReferenceView;
            presentationController.sourceRect = childOfContainerView.frame;
        }
    }];
}

Solution 3 - Ios

If wanted directly from Button Action then this code can be used

SecondViewController *destinationViewController = (SecondViewController *)[self.storyboard instantiateViewControllerWithIdentifier:@"second"];
destinationViewController.modalPresentationStyle = UIModalPresentationPopover;
destinationViewController.popoverPresentationController.sourceView = self.customButton;

// Set the correct sourceRect given the sender's bounds
destinationViewController.popoverPresentationController.sourceRect = ((UIView *)sender).bounds;
[self presentViewController:destinationViewController animated:YES completion:nil];

Solution 4 - Ios

EDIT: Regarding the down-votes. First a fun and relevant story:

http://www.folklore.org/StoryView.py?story=Negative_2000_Lines_Of_Code.txt

I posted the answer below in order to help coders that might have been stuck in the mindset that iPad/iPhone still needed separate execution paths when presenting a media picker UI. At the time of my original post this was not clear in the other answers. This solution path simplified my code and the future maintenance of my App. I think others might find this perspective useful because sometimes removing the right lines of code can be a good solution.

End of edit.

If you already have a working program and just want to get rid of the depreciation warning this could work for you.

In my code, and in my understanding, Popovers were introduced for the iPad and are iPad specific. Apple seems to have changed that. So, if you already have a working program that uses something like this:

if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
    // use popovers 
else
    //  don't use popovers

what you can do is just get rid of the iPad specific code (which is probably the code using Popovers) and have your program run the same instructions for both iPhone and iPad. This worked for me.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVarun NahariaView Question on Stackoverflow
Solution 1 - IosMidhun MPView Answer on Stackoverflow
Solution 2 - IosqixView Answer on Stackoverflow
Solution 3 - IosAbhishekView Answer on Stackoverflow
Solution 4 - IosMark NilsenView Answer on Stackoverflow