UIPopoverPresentationController has wrong anchor position on iPhone 6 plus and 7 plus when it's in landscape mode [duplicate] - objective-c

In my iOS 8 app, this popover segue appears correctly on all devices in all orientations except for iPhone 6 Plus landscape:
This is how it looks on iPhone 6 Plus landscape (it is stretching almost from top to bottom):
And when it displays like this, clicking outside of the view doesn't dismiss it (although Cancel does work). Rotating back to portrait gets it back to normal.
All of the constraints in this UIViewController are installed on all size classes.
When debugging values in viewDidAppear: I see the following:
po self.view: frame = (0 0; 250 394)
po self.preferredContentSize (width = 250, height = 160)
What is causing the view's height to jump to 394?
I'm actually having the same issue with another popover segue in iPhone 6 Plus landscape as well. (And in case there was curiosity, I'm using a VC instead of 'UIAlertController' here because of the validation requirements of the UITextField displayed don't work well with UIAlertController.)
Edit to include my popover code:
This code is found in prepareForSegue:
FavoriteNameViewController *nameVC = segue.destinationViewController;
UIPopoverPresentationController *popPC = nameVC.popoverPresentationController;
popPC.delegate = self;
nameVC.delegate = self;
nameVC.view.center = self.originalContentView.center;
And then the delegate method:
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
return UIModalPresentationNone;
}
And here is the segue definition in Xcode:

What you're seeing is not a popover. It's a normal presented view. By default, a popover appears as a popover on iPad, but as a presented view on iPhone — including the iPhone 6 plus. On other iPhones, this presented view is fullscreen - it covers everything. But the iPhone 6 is so wide that they don't do that, so it appears in the middle of the screen at a standard width (the width of a smaller iPhone).
Thus, the preferred content size has no effect. This isn't a popover. Presented view controller views are given a standard size, and this one is no exception.
However, you can have the popover appear as a popover on iPhone. To do so:
Set a delegate for the popover view controller's popoverPresentationController before presenting it.
In the delegate, implement adaptivePresentationStyleForPresentationController: and return .None.
However, this is apparently not working on iPhone 6 Plus in landscape mode; the popover is not "adapting". I would describe this as a bug!
EDIT In iOS 9, the problem is solved! Implement the new delegate method adaptivePresentationStyleForPresentationController:traitCollection: to return .None and you'll get a popover under all circumstances, including the iPhone 6 Plus in landscape. Here's a complete working example where the popover is created and summoned in code in response to a button tap:
#IBAction func doButton(sender: AnyObject) {
let vc = MyViewController()
vc.preferredContentSize = CGSizeMake(400,500)
vc.modalPresentationStyle = .Popover
if let pres = vc.presentationController {
pres.delegate = self
}
self.presentViewController(vc, animated: true, completion: nil)
if let pop = vc.popoverPresentationController {
pop.sourceView = (sender as! UIView)
pop.sourceRect = (sender as! UIView).bounds
}
}
func adaptivePresentationStyleForPresentationController(
controller: UIPresentationController,
traitCollection: UITraitCollection)
-> UIModalPresentationStyle {
return .None
}

Related

iOS 8 - Modal in Popover

I have a popover with TabBarController in it. In one tab there is a TableViewController with a list of names. And there is a plus button, that has a modal segue to AddCharacterVC for adding new names.
In iOS 7 I do it like this:
AddCharacterViewController *acvc = (AddCharacterViewController *)segue.destinationViewController;
acvc.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
acvc.modalPresentationStyle = UIModalPresentationCurrentContext;// so it does not take full screen in popover
And in AddCharacterVC I set content size like this:
- (void)viewWillAppear:(BOOL)animated {
CGSize size = CGSizeMake(320, 480); // size of view in popover
if (IDIOM == IPAD && [self respondsToSelector:#selector(preferredContentSize)]){
self.preferredContentSize = size;
}
And it works perfectly.
However, in iOS 8 modal view does not cover the whole popover, leaving TabBar visible. The user can tap on it or not, anyway modal view won't unwind properly.
I've tried:
setting acvc.modalPresentationStyle to UIModalPresentationOverCurrentContext
tried to set TabBar hidden
checked in storyboard that edges of TableVC extend under Bottom Bar and Bottom Bar in Modal View (AddCharacterVC) is set to none
All with no results.
Now the only thing I can think of is to try making modalPresentationStyleCustom and use UIPresentationController (I'm trying to do it now, but I haven't done it before). Am I missing something? Could there be other way to do it? Sorry, I cannot post images here yet. Many thanks in advance!
Ok, so I've set the modalPresentationStile to UIModalPresentationCustom, and used UIPresentationController - I've just copied code from WWDC-14's LookInside project and modified it a bit.
I'm not sure if it was the best solution, but it worked in my case.

How to resize UIPageViewController in landscape

I am having an issue with resizing UIPageViewController in landscape mode.
Here is my scenario:
I have a UIViewController which has a scrollview in it.
I added a UIPageViewController to the scrollview programmatically and it is displaying all my view controllers in each page correctly.
When i change the device orientation to landscape i am correctly changing the content size of the scrollview but the PageViewController is displaying only up to half of its contents. Can you please help me with this one.
Thanks,
Anand.
It looks like you are either not getting the correct orientation of your device or trying to set it in the viewDidLoad function. The following code should correctly set your page view controller's frame to current orientation.
- (void)viewWillLayoutSubviews {
CGRect screenFrame = [[UIScreen mainScreen] applicationFrame];
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
screenFrame = CGRectMake(0, 0, screenFrame.size.height, screenFrame.size.width);
}
//Please don't forget to replace _myPageViewController with your variable
_myPageViewController.frame = screenFrame;
}
Please note that code is getting called from viewWillLayoutSubviews
function.

Tab Bar covers TableView cells in iOS7

I have a custom tableViewController that I'm adding to a TabBarController with
self.tabBarController.viewControllers = [NSArray arrayWithObjects:someOtherViewController, customTableViewController, nil];
self.tabBarController.selectedIndex = 1;
The issue I'm having is that the last 1.5 tableViewCells are being covered by the tab bar at the bottom of the screen on an iPhone 4 running iOS7. When I use the iOS Simulator - iPhone Retina (4-inch) / iOS 7.0 the issue still exists.
What is the correct way to make the tableView line up with the top of the tabBar at the bottom of the screen without using 'magic numbers'?
Try this for your CustomViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
UIEdgeInsets adjustForTabbarInsets = UIEdgeInsetsMake(0, 0, CGRectGetHeight(self.tabBarController.tabBar.frame), 0);
self.scrollView.contentInset = adjustForTabbarInsets;
self.scrollView.scrollIndicatorInsets = adjustForTabbarInsets;
}
It's an iOS 8 solution but it may work on iOS 7 to: Go to storyboard > select table view controller > uncheck "Under Bottom Bars". That's it!
Setting the contentInset of your table view with a .bottom value of 49 points should correct this.
Under the right configurations, setting YES for the new UIViewController property on iOS 7 called automaticallyAdjustsScrollViewInsets should correct this, but (again) it depends upon a lot of other factors (view hierarchy, parent view controller's settings, et cetera).
The accepted answer doesn't quite work for me--my set up is a little different. I'm programatically creating my view controllers. My app's root is a tab bar controller, one tab is a navigation controller, whose root is a UIViewController with a table view as the main view.
What works for me though is when I manually computed the table view's height and set it in the frame when alloc-initing the table view. The general formula is:
screen height - (status bar height + nav bar height + tab bar height)
CGFloat bottom = self.tabBarController.tabBar.frame.size.height;
NSLog(#"%f",bottom);
[self.tableview setScrollIndicatorInsets:UIEdgeInsetsMake(0, 0, bottom, 0)];
self.tableview.contentInset = UIEdgeInsetsMake(0, 0, bottom, 0);
Embed your table controller in a navigation controller.
1. select the view in story board.
2. On menu bar select Editor -> embed in -> navigation controller.
Hope that helps
I have a similar view hierarchy to Matt Quiros: UITabBarController -> UINavigationController -> UIViewController -> UITableViewController (embedded as a subview of the UIViewController). The other answers didn't work in my case, and I had to set the table view's frame manually in the table view controller's viewWillAppear: method.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Adjust height of tableview (does not resize correctly in iOS 7)
CGRect tableViewFrame = self.tableView.frame;
tableViewFrame.size.height = [self heightForTableView];
self.tableView.frame = tableViewFrame;
}
- (CGFloat)heightForTableView
{
return CGRectGetHeight([[UIScreen mainScreen] bounds]) -
(CGRectGetHeight([[UIApplication sharedApplication] statusBarFrame]) +
CGRectGetHeight(self.navigationController.navigationBar.frame) +
CGRectGetHeight(self.tabBarController.tabBar.frame));
}
If anyone finds a better solution, please share!
I think this would work better for you:
After [super viewDidLoad];
try the following code:
if ([self respondsToSelector:#selector(edgesForExtendedLayout)])
self.edgesForExtendedLayout = UIRectEdgeNone;
You can also implement viewDidLayoutSubviews and use bottomLayoutGuide to get the height of the tab bar:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
CGFloat bottomOffset = self.bottomLayoutGuide.length;
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, bottomOffset, 0);
}
Even though changing the contentInset of your table View is a working solution, I find it better to make sure your table view stops before the Tabbar.
As Paul Newman said, using the bottomLayoutGuide is a good thing, specially if you are using autolayout.
In My case adding a constraint to the bottom of the tableview linking to the top of the BottomLayoutGuide was a clean solution, this is an example with Storyboard, but it can be done in code as well.
Hope it helps.

UIDatePicker at bottom of a modal, in iOS5 & iOS6

In my app, I show to my users a modal with an UIDatePicker at the bottom so they can select a date. My modal has to work in portrait and in landscape. Also, my DAtePicker has to be smaller in landscape than in portrait, or it takes nearly all the screen. I'm using the following code when the screen is rotating.
if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) {
datePicker.frame = CGRectMake(datePicker.frame.origin.x, myView.frame.size.width - 216.0, datePicker.frame.size.width, 216);
}
else {
datePicker.frame = CGRectMake(datePicker.frame.origin.x, myView.frame.size.height - 160.0, datePicker.frame.size.width, 160);
}
It does work great on iOS6 : the DatePicker is always where I want it to be. But in iOS5, the same DatePicker is in the middle of the screen on portrait mode, and off screen in the landscape mode.
Do you know a solution which works on iOS 5 and iOS 6?
Autorotation mechanism has changed in iOS 6.
From iOS 6.0 Release Notes:
The willRotateToInterfaceOrientation:duration:,
willAnimateRotationToInterfaceOrientation:duration:, and
didRotateFromInterfaceOrientation: methods are no longer called on any
view controller that makes a full-screen presentation over itself—for
example, presentViewController:animated:completion:. You should make
sure that your apps are not using these methods to manage the layout
of any subviews. Instead, they should use the view controller’s
viewWillLayoutSubviews method and adjust the layout using the view’s
bounds rectangle.
I think you should implement the iOS 5 specific methods to make it work.

iPad - xib ignoring orientation on view loads

iPad question:
We have views that we're initializing from .xibs, with each .xib containing both landscape and portrait layouts.
The first view we load respects the orientation of the device, but any subsequent views seem to have confusion over which orientation they should be using. Or, rather, they seem to ignore orientation altogether and go with whichever orientation the .xib file was saved with. (i.e. We saved a .xib file in landscape, and even though the device was held in portrait position, it loaded the view from the .xib with a landscape layout.)
This is how we're calling in these views:
Settings *settingsTEMP = [[Settings alloc] init];
self.settings = settingsTEMP;
[self.view insertSubview:settings.view atIndex:0];
[settingsTEMP release];
The first of the calls (the one that respects the device's orientation) is in the viewDidLoad. The second, which uses a different view (the one that doesn't respect the device's orientation) is in a method that's called after a button is pressed, but uses the same syntax.
If we put both into viewDidLoad, they both respect the orientation.
We've tried searching for anyone having similar issues but have been thus far unsuccessful. Thanks in advance for any help you can provide.
I use the following
UIInterfaceOrientation toInterfaceOrientation = self.interfaceOrientation;
if ((toInterfaceOrientation == UIDeviceOrientationLandscapeLeft) ||
(toInterfaceOrientation == UIDeviceOrientationLandscapeRight))
{
NSLog(#"vSettings UIDeviceOrientationLandscape");
CGRect contentRect = CGRectMake(0,0,1024,768);
vSettings.bounds = contentRect;
CGRect myFrame = vSettings.frame;
myFrame.origin.x = 0.0;
myFrame.origin.y = 0.0;
vSettings.frame = myFrame;
UIImage *image;
image = [UIImage imageNamed: #"Default-Landscape.png"];
[backGroundSettings setImage:image];
}
I hate having to do this as it seems a right fudge but it works for me. Called just after I create the view.