I am working on universal iPhone/iPad application with universal storyboard. For some ViewControllers I'm using size classes if they has some specific layout on iPad.
I have one ViewController that needs to be presented modally on iPhone but on iPad it needs to be shown in UIPopoverController.
UINavigationController *navigationController = [self.storyboard instantiateViewControllerWithIdentifier:#"ComposeMessageNavigationController"];
ComposeMessageViewController *viewController = (ComposeMessageViewController *)navigationController.topViewController;
//Prepeare my view controlller
...
if (IS_IPAD) {
UIPopoverController * popover = [[UIPopoverController alloc] initWithContentViewController:navigationController];
CGSize screenSize = [UIScreen mainScreen].bounds.size;
CGRect popoverFrame = CGRectMake(screenSize.width / 2, screenSize.height / 2, 1, 1);
[popover presentPopoverFromRect:popoverFrame inView:self.view permittedArrowDirections:0 animated:YES];
} else {
[self presentViewController:navigationController animated:YES completion:^{
}];
}
It works pretty good but there is problem with Size Classes. I made some changes on storyboard at wRegular/hRegular Size Class but on iPad in UIPopoverController still showing iPhone layout. It's because of size of popover is lower then iPad screen. Can I make my changes in Interface Builder with Size Classes to show them in popover on iPad but ignore on iPhone?
Using setOverrideTraitCollection on the popover view controller should allow you to use the regular x regular for an iPad.
Related
On iPad UIPopoverPresentationController working fine but on iPhone it is always showing full window modal popup. i am using following code:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
MySecondViewController *contentVC = [storyboard instantiateViewControllerWithIdentifier:#"Pop"];
contentVC.modalPresentationStyle = UINavigationControllerOperationPop; // 13
UIPopoverPresentationController *popPC = contentVC.popoverPresentationController; // 14
contentVC.popoverPresentationController.sourceRect =CGRectMake(100, 130, 280, 230);
self.navigationController.preferredContentSize = CGSizeMake(200, self.parentViewController.childViewControllers.lastObject.preferredContentSize.height-100);
//self.showPop.frame; // 15
contentVC.popoverPresentationController.sourceView =
self.showPop; // 16
popPC.permittedArrowDirections = UIPopoverArrowDirectionAny; // 17
popPC.delegate = self; //18
[self presentViewController:contentVC animated:YES completion:nil];
-(UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
return UIModalPresentationNone;
}
In ViewController.h Firstly make a property of UIPopoverPresenatationController.
#property(nonatomic,retain)UIPopoverPresentationController *dateTimePopover8;
Then to show PopOverPresentationcontroller
UINavigationController *destNav = [[UINavigationController alloc] initWithRootViewController:dateVC];/*Here dateVC is controller you want to show in popover*/
dateVC.preferredContentSize = CGSizeMake(280,200);
destNav.modalPresentationStyle = UIModalPresentationPopover;
_dateTimePopover8 = destNav.popoverPresentationController;
_dateTimePopover8.delegate = self;
_dateTimePopover8.sourceView = self.view;
_dateTimePopover8.sourceRect = [sender frame];
destNav.modalPresentationStyle = UIModalPresentationPopover;
destNav.navigationBarHidden = YES;
[self presentViewController:destNav animated:YES completion:nil];
You must have noticed that we are presenting View Controller instead of presenting popOver.So we have to hide this in new way also.It hides automatically when we click on screen.
-(void)hideIOS8PopOver
{
[self dismissViewControllerAnimated:YES completion:nil];
}
We have to implement the delegate of UIPopoverPresenatationController in implementation file.Write below delegate method in implementation file.
- (UIModalPresentationStyle) adaptivePresentationStyleForPresentationController: (UIPresentationController * ) controller {
return UIModalPresentationNone;
}
Popover controllers are for use exclusively on iPad devices.
Edit: As stated by Soberman, since iOS 8 it is possible to present popovers on iPhone using public APIs, so this answer is probably not relevant anymore.
As stated in Apple's documentation on UIPopoverController:
Popover controllers are for use exclusively on iPad devices.
So there is no way to use this class in iPhone application unfortunately. But there are a couple of custom third-party implementations of the functionality provided by UIPopoverController which add iPhone support and more. See https://github.com/50pixels/FPPopover for example.
Edit: There also is another highly customizable popover implementation for both iPhone/iPad worth checking out: https://github.com/nicolaschengdev/WYPopoverController.
In iOS 8.3, I present a view controller with the following code:
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:controller];
nav.modalPresentationStyle = UIModalPresentationFormSheet;
nav.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:nav animated:YES completion:nil];
Upon presentation in my iPad simulator, I check the UITraitCollection property of my view (po self.traitCollection) in the viewDidLoad() method and here is what I get:
<_UITraitNameHorizontalSizeClass = Regular,
_UITraitNameVerticalSizeClass = Regular>
I switch the simulator from iPad to iPhone (5s or 6) and I still get the Regular horizontal size class trait in my view. iPhones should have an Compact horizontal size class trait.
Since the horizontal size class is wrong, the wrong size class design is loaded from the storyboard. Any ideas why the framework is providing a wrong size class?
I just ran into a very frustrating problem. I don't know if it's an iOS8 bug or it's something else.
I'm loading the view from a nib, which looks like this:
Here ist my code:
UIViewController *popoverViewController = [[UIViewController alloc] init];
[popoverViewController setView:poppverViewFromNib];
_popoverController = [[UIPopoverController alloc] initWithContentViewController:popoverViewController];
[_popverController presentPopoverFromRect:CGRectMake(100, 100, 10, 10) inView:[self view] permittedArrowDirections:UIPopverArrowDirectionAny animated:YES];
The iOS simulator as well as the real device are displaying the viewController modally like you can see here:
EDIT
As requested in comments, I tell you what I expect when using UIPopoverController:
I expect an UIPopover, not a modalViewController.
So here my question to all of those how didn't get it: Why does my UIPopoverController does not display as a popover but as modalViewController? How can I fix it?
Read the documentation. Popover presentation is only available for horizontally regular size classes. Currently, this is only on iPad. Popover presentation on iPhone will always be executed as full screen modal presentation. See here.
You are lucky that iOS8 doesn't crash, as iOS7 and below, if you used UIPopoverController on a phone idiom, your app would crash. UIPopoverController is deprecated in iOS8 in favor of popover modal presentation. This is why your app does not crash.
You could keep the same code for UIPopoverController as iOS 7
just set ViewController.preferredContentSize = CGSizeMake(300, 290);
It will work.
Other Solution
iOS 8 has a new PopOverController called UIPopoverPresentationController
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UINavigationController *navController = [storyboard instantiateViewControllerWithIdentifier:#"Terms_NC"];
navController.preferredContentSize = CGSizeMake(300, 290);
// Present the view controller using the popover style.
navController.modalPresentationStyle = UIModalPresentationPopover;
[self presentViewController:navController animated: YES completion: nil];
// Get the popover presentation controller and configure it.
UIPopoverPresentationController *presentationController =
[navController popoverPresentationController];
presentationController.permittedArrowDirections =0;
presentationController.sourceView = self.view;
presentationController.sourceRect = CGRectMake(100, 100, 300, 340);
For my iPad version of the app I want to open my UIViewController as a UIPopoverController from the click of a button. So, the real code for view controller opening is below. How can I easily convert such code into opening a UIViewController as a UIPopoverController?
Code:
UIStoryboard *sb = [UIStoryboard storyboardWithName:[[NSBundle mainBundle].infoDictionary objectForKey:#"UIMainStoryboardFile"] bundle:[NSBundle mainBundle]];
UIViewController *aboutView = [sb instantiateViewControllerWithIdentifier:#"AboutViewController"];
aboutView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:aboutView animated:YES completion:NULL];
Use something like:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
UIPopoverController *poc = [[UIPopoverController alloc] initWithContentViewController:aboutView];
[poc presentPopoverFromRect:CGRectMake(somePoint.x, somePointY, 1.0, 1.0) inView:self.view permittedArrowDirections: UIPopoverArrowDirectionAny animated:YES];
} else {
aboutView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:aboutView animated:YES completion:NULL];
}
You can also present the popover from a button and you also can control the direction in which the popover will be presented. If you use UIPopoverArrowDirectionAny, iOS will make a smart decision for the popover to be visible.
You should also keep a strong reference to your popover and only present it while it is nil to make sure the popover is only present once. That means if the popover is dismissed, set the property holding it to nil.
A common solution is to have two storyboards, one for iPhone, one for iPad. That way you can use the popover segue in the iPad storyboard and the modal segue in the iPhone storyboard. You can readily arrange your Info.plist so that the correct storyboard loads automatically at launch time. You will still need some conditional code, though, since your code will respond differently to having a presented view controller than having a popover.
I'm working on an app that is supposed to be universal, one app for both iPad and iPhone. I would like to keep their interfaces as similar as possible. In the iPhone app I am using a Tab bar controller, and one of those tabs goes to an image picker controller. Obviously I cannot do that in iPad. So I have hijacked control of that button to bring a popupcontroller that has the image picker in it. This all works well enough, except when I bring up the popup, it is not in the correct place. When I rotate the simulator, the popup goes to the correct place, and stays when I rotate back even.
My code is based on the code in this question:
Ipad UIImagePickerController and UIPopoverController error
Why would my popup not be in the correct location?
If your code is based on the question you referenced, it would appear you are using the following code to show the popover:
[popoverController presentPopoverFromBarButtonItem:sender
permittedArrowDirections:UIPopoverArrowDirectionUp
animated:YES]
UIPopoverController:presentPopoverFromBarButtonItem:permittedArrowDirections:animated accepts a UIBarButtonItem* for the sender which your UITabBar does not have. UITabBar uses UITabBarItem which has a base of UIBarItem. UIBarButtonItem also has that base (UIBarItem).
Anyhow... I also needed to show a uipopovercontroller from a tabbaritem, I used the following code:
MyViewController *myVC = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:[NSBundle mainBundle]];
UIPopoverController *popover = [[UIPopoverController alloc] initWithContentViewController:myVC];
[myVC release];
popover.popoverContentSize = myVC.view.frame.size;
popover.delegate = self;
int tabBarItemWidth = self.tabBar.frame.size.width / [self.tabBar.items count];
int x = tabBarItemWidth * 6;
CGRect rect = CGRectMake(x, 0, tabBarItemWidth, self.tabBar.frame.size.height);
[popover presentPopoverFromRect:rect
inView:self.tabBar
permittedArrowDirections:UIPopoverArrowDirectionUp
animated:YES];
Note: your x calculation will be different. The tab bar item selected for me was the 6th one.
Basically
x = tabBarItemWidth * currentTabBarItemIndex;