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.
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.
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;
}
}];
}
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];
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.
Related
The following code should popover a view and the view size should be customized as I customized it by code, but when I run it, I see it's not customized.
Can anyone help?
- (IBAction)barButtonLeft:(id)sender {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
_myViewController = [storyboard instantiateViewControllerWithIdentifier:#"Sort"];
_myViewController.modalPresentationStyle = UIModalPresentationPopover;
self.myViewController.modalPresentationStyle = UIModalPresentationFormSheet;
self.myViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
self.myViewController.preferredContentSize = CGSizeMake(139, 70);
[self presentViewController:_myViewController animated:YES completion:nil];
UIPopoverPresentationController *popController = [_myViewController popoverPresentationController];
}
You're using _myViewController and self.myViewController together here, which is a little confusing but I assume you have a property, myViewController, backed by an instance variable, _myViewController? I'm therefore assuming both of these things are actually pointing to the same instance.
For clarity I'd recommend sticking with one or the other, probably property access.
If they are both pointing to the same instance, then what the code above is doing is setting a popover presentation style, then setting a form sheet presentation style. The last one you set will "win", so the view controller will be presented as a form sheet (on iPads, this will be a box in the middle of the screen, on iPhones, it will be full screen).
If you are running this code on an iPhone, then popovers don't work by default anyway - they will be presented full screen. To prevent that, you need to set a delegate to the popover presentation controller, implement - (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection;, and return UIModalPresentationNone.
You must also set a source view or bar button item on the popover presentation controller before attempting the presentation, or your app will crash.
I've recently been updating an application to replace instances of the deprecated UIPopoverController with UIViewControllers presented modally, and when I create one, it takes about 250% as long to appear. Also, setting animated:NO in the presentViewController method also seems to have no effect.
Does anyone know why this might be?
Here's some of the relevant code I've written (it's in an IBAction from a UIButton being pressed):
UIViewController *popover = [[MyViewControllerForPopover alloc] initWithNibName:nibName bundle:nil];
popover.modalPresentationStyle = UIModalPresentationPopover;
popover.preferredContentSize = popover.view.frame.size;
popover.popoverPresentationController.sourceView = parentView;
popover.popoverPresentationController.sourceRect = location;
popover.popoverPresentationController.permittedArrowDirections = arrows;
[parentViewController presentViewController:popover animated:NO completion:nil];
I tried setting modalTransitionStyle, but it didn't seem to have any effect on the style or duration of animation. What I see is a delay, followed by the animation, so it looks like there's an extended amount of time spent setting up the view controller.
This is the same view controller that used to be put inside the UIPopover, via the initWithContentViewController init method, so I can't understand why this is slower.
Thanks
On iOS6 I had a method to make one view controller in my navigation-style app auto rotate to landscape when I pushed it. (Basically present a bogus view controller and dismiss it in viewWillAppear).
UIViewController *mVC = [[UIViewController alloc] init];
[self presentModalViewController:mVC animated:NO];
if (![mVC isBeingDismissed])
[self dismissModalViewControllerAnimated:NO];
With the latest SDK this no longer works. Does anyone have another way to auto rotate?
Turns out the solution is simple, just pass YES to dismissModalViewControllerAnimated
UIViewController *mVC = [[UIViewController alloc] init];
[self presentModalViewController:mVC animated:NO];
if (![mVC isBeingDismissed])
[self dismissModalViewControllerAnimated:YES]; //Fix here
From Developer site
"When a view controller is presented over the root view controller, the system behavior changes in two ways. First, the presented view controller is used instead of the root view controller when determining whether an orientation is supported. Second, the presented view controller can also provide a preferred orientation. If the view controller is presented full screen, the user interface is presented in the preferred orientation. The user is expected to see that the orientation is different from the device orientation and rotate the device. A preferred orientation is most often used when the content must be presented in the new orientation."
I think here you can use the preferred orientation method here.
I have a couple of modal views that have been working "just fine" and now stopped returning to the parent view controller and "the code has not changed." -- classic problem description.
I debugged the modal view dismissing and the parent view controller is nil, which explains the problem, but not the cause. I did upgrade my SDK from 4.1.2 to 4.2 so I can start working with iOS 5. I am suspect of the new memory management ARC and my style of autorelease versus retain/release.
Following is the code from my rootview controller to the AboutViewController:
- (IBAction)doInfo:(id)sender {
NSLog(#"%s", __FUNCTION__);
AboutViewController *aboutViewController = [[[AboutViewController alloc] initWithNibName:#"AboutViewController" bundle:[NSBundle mainBundle]] autorelease];
if (aboutViewController) {
aboutViewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
aboutViewController.hidesBottomBarWhenPushed = YES;
self.navigationController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
self.navigationController.navigationBarHidden = YES;
[self presentModalViewController:aboutViewController animated:YES];
}
}
Following is the dismiss in the AboutViewController back to its parent after pressing a "Done" button.
- (IBAction)doDone:(id)sender {
NSLog(#"%s", __FUNCTION__);
[[self parentViewController] dismissModalViewControllerAnimated:YES];
}
OK ... I fixed by changing as follows. Now the question is why did this work before?
- (IBAction)doDone:(id)sender {
NSLog(#"%s", __FUNCTION__);
[self dismissModalViewControllerAnimated:YES];
}
Quoting from [1] about the property parentViewController:
Prior to iOS 5.0, if a view did not have a parent view controller and was being presented modally, the view controller that was presenting it would be returned. This is no longer the case. You can get the presenting view controller using the presentingViewController property.
So this resolves the issue why this did work before and does not anymore. If building for iOS 5, I would use the presentingViewController property, as it is advised by the docs to let the parent view controller (or in this case presenting) dismiss the modal view. However if building for iOS 4 and 5, I would let your code in place as it forwards the call to the appropriate view controller anyway.
If in any case the app ran flawlessly on iOS 5 before recompiling, I would assume that Apple put some runtime-trickery in place that mimics the old behaviour for apps compiled pre-iOS5.
[1] http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html
I have built a category that add presentingViewController on iOS 4.
It disables itself on iOS 5.
You can use it seamlessly. Please see backward-modal.
I hope this benefits you as much as it does to me; It makes your code more clean!
I am trying to display a modal viewController in an iPad app using the UIModalPresentationFormSheet view style. I am looking to produce something similar to the Mail app's new message UI/animation.
There are two things that are not behaving correctly:
The modal viewController that is presented always animates to y=0, i.e. to the very top of the
view and not some pixels below the status bar as it does in the mail app.
The documentation says:
UIModalPresentationFormSheet The width
and height of the presented view are
smaller than those of the screen and
the view is centered on the screen. If
the device is in a landscape
orientation and the keyboard is
visible, the position of the view is
adjusted upward so that the view
remains visible. All uncovered areas
are dimmed to prevent the user from
interacting with them.
However, in my case there is no dimming and I can still interact with the parentView below the modalViewController.
The controller that presents the modalView I do this:
AddNewItemViewController *newItemViewController = [[AddNewItemViewController alloc] initWithNibName:#"AddNewItemViewController" bundle:nil];
[self presentModalViewController:newItemViewController animated:YES];
[newItemViewController release];
In the viewController being presented I do this:
- (void)viewDidLoad {
[nameField becomeFirstResponder];
[self setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self setModalPresentationStyle:UIModalPresentationFormSheet];
[super viewDidLoad];
}
I hope someone can help me out.
Is there some other properties I need to set on the parent and modalViewController?
Is the viewDidLoad not the right place to do this setup?
Thanks in advance:)
You set the transition and presentation styles when you create the modal view, before you call presentModalViewController. Remember, the view that creates the modal view 'owns' that object. You want the owner to set these properties because you might implement this modal view elsewhere in the app and want different transition or presentation styles. This way, you set it each time as appropriate.
AddNewItemViewController *newItemViewController = [[AddNewItemViewController alloc] initWithNibName:#"AddNewItemViewController" bundle:nil];
newItemViewController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:newItemViewController animated:YES];
[newItemViewController release];
You're right in calling becomeFirstResponder in viewDidLoad.