UIPopoverPresentationController not showing in the right position - ios

I have a popover where the SourceRect is the setting button (the button with a gear icon).
The popover position is correct for all device orientation except for LandscapeLeft.
I set the anchor from XCode Interface builder and set the source rect programmatically:
var poc = segue.DestinationViewController as PVPencilOptionsController;
poc.ViewModel = this.ViewModel.ColorPicker;
var ppc = (UIPopoverPresentationController)poc.PresentationController;
ppc.SourceRect = this.PVEditIssuePhotoPencilOptionsButton.Frame;
Below are the result of popover position:
Portrait/upside down (correct):
Landscape Right (correct):
Landscape Left (wrong)
As you can see, if the device orientation is Landscape Left, there is a gap between the popover arrow and the setting button.
How to find out where this gap come from?

I've had simlar issues and mine was all due to the fact that I was calculating my sizing and my positioning in the wrong place, sometimes using the wrong values as well. There's two things I would suggest to find your problem:
1. Do all position/sizing calculations in LayoutSubviews: Override the LayoutSubviews in your controller and calculate your positioning information in there. After the base call.
2. Log out dimensions of Frame as well as Bounds: This way you can see what you have and where you want it to be. Do this before and after layout subviews to see the difference.

Related

iOS Toolbar Height Depends on Initial Orientation

I am facing some trouble with a bottom UIToolbar on the iPhone. The height of the bar seems to depend on the device orientation at the time when I navigate to the scene and does not update when the orientation is changed.
When I navigate to the scene in portrait mode, the bottom bar has a height of 44. When I then turn the phone (an iPhone XR here), the height of the bar remains 44.
When I open the scene in landscape mode, the bottom bar height is 49 and also remains 49 when I turn the phone upright.
This can easily be reproduced with as simple 2-scene app such as this one:
Initially, this was not really an issue - the user would not even notice the small change. But now I am using the bottom bar in a split view. When that is initially opened in portrait, the bottom toolbar has a height of 44. Turning the phone into landscape, the detail view with its own toolbar, 49 high, opens. Then I have two toolbars with different heights right next to each other, which is rather ugly:
So the question is how I can ensure the toolbar height is either updated on orientation change, or the height is always the same (as e.g. in the email app). I don't want to hard-code the height expecting that it would eventually make things worse on future iOS versions or different devices.
I am using Xcode 10.1, running the app on an iPhone with iOS 12.1.2.
Did you add the toolbar manually on to the view or are you using the navigationController to manage the toolbar? I am assuming you did, since on rotation the height is not changing.
The navigation controller manages the height of the toolbar on rotation. Adding the following to the viewDidLoad() method will show the navigationController's toolbar.
navigationController?.setToolbarHidden(false, animated: false)
This approach requires a little less code than if your view controller manages the toolbar (one less method, one less outlet).
Here is the default template I did to check that the toolbar displayed properly on iPad and an iPhone Max model:
https://github.com/joelesli/TBHeight/
iPad Pro
iPhone XS Max
iPad Air 2
iPhone 8 Plus
I initially solved it (based on Mocha's comment above) by overriding viewWillTransition(...) to redraw the toolbar and assume its default dimensions for each orientation.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if let bBar = bottomBar { bBar.invalidateIntrinsicContentSize() }
}
That way it should adapt to future UI changes, and I don't risk having problems in app approval (if modifying standard UI element styles would be an issue here).
Update:
I prefer JoelEsli's solution though this might be a good alternative in some situations, so I'm leaving it here for completeness.

Height for portion below Tabbar in Iphone X

Hoping someone can help me with the height below the Tabbar in Iphone X. I need to remove this height in one of my frame sizes for IPhone X. The new status bar has a different height and I can reference that height with:
UIApplication.shared.statusBarFrame.height
Does anyone know how I can reference the height below the Tabbar in Iphone X?:
I think this is what you are looking for:
if #available(iOS 11.0, *) {
print(view.safeAreaInsets.bottom)
}
This is because of the new safe area introduced in iOS 11, and will give the space you are looking for on the iPhone X. It will vary by device however.
I don't know why you want this indicator to be hide. But as per Apple recommended guidelines, it's mentioned to not cover this space/area with other views.
Don't mask or call special attention to key display features. Don't
attempt to hide the device's rounded corners, sensor housing, or
indicator for accessing the Home screen by placing black bars at the
top and bottom of the screen. Don't use visual adornments like
brackets, bezels, shapes, or instructional text to call special
attention to these areas either.
Link - Interface guidelines for iPhone-X
But, if your requirement is to play Video in a Landscape mode.
You can auto-hide a indicator while playing. It will reappear automatically when user will touch the screen while video play.
For auto-hide, You can override prefersHomeIndicatorAutoHidden in respective ViewController.
override func prefersHomeIndicatorAutoHidden() -> Bool {
return true
}
To calculate the free space between the nav bar and the tab bar you can convert their coordinates to your VC's view's space and then just subtract.
I find this easier than trying to add together all the possible things that will affect the safe are. Plus it has the benefit of being compatible with iOS versions before the SafeAreaInset was introduced.
CGRect navBarFrame = [self.view convertRect:self.navigationController.navigationBar.bounds fromView:self.navigationController.navigationBar];
CGRect tabBarFrame = [self.view convertRect:self.tabBarController.tabBar.bounds fromView:self.tabBarController.tabBar];
return CGRectGetMinY(tabBarFrame) - CGRectGetMaxY(navBarFrame);

If ViewController is embedded in TabBarController, some of the content is not tappable

I have a test project and created ViewController which is embedded in TabBarController. I've placed 'UISearchBar' and 'UISegmentedControl' with constraints and in portrait mode they are clickable. But when I rotate to landscape mode(iPhone) only 320 pixels of the searchBar and segmentedControl is clickable, the rest isn't. This is better explained in the figures below.
Why is this happening?
How should I "stretch" the clickable area in landscape mode?
As you can see the constraints are working fine, the components have their correct sizes.
Some of your constraints are working fine, but very likely others are not. If the segmented control is only partially tappable in landscape, you should check the frame sizes for it's superview(s).
It looks like in landscape mode the segmented control is partially outside the superview frame. If the superview frame is ok, look further into the superview hierarchy.
To fix it, try this:
Select all the components in IB (search bar + segmented control + date picker)
Open menu 'Editor' -> 'Resolve Auto Layout Issues' -> 'Reset to Suggested Constraints'
I've just put the following code in mine TabBarViewController class.
- (void)viewDidLayoutSubviews
{
// fix for iOS7 bug in UITabBarController
self.selectedViewController.view.superview.frame = self.view.bounds;
}

Xcode Layout Not Appearing Right In Simulator or Real Device

I have a layout working fine in Xcode storyboard for a Master-Detail splitview app but when I run it in the Simulator or on an actual device it appears slightly messed up and I have no idea why.
The image in Xcode looks like this;
The layout in Simulator and Device looks like this
This is almost certainly a problem with the autoresizing settings of your subviews (aka "Springs and Struts").
You are building a UISplitViewController-based application. Note that the dimensions of your Detail View Controller's frame are different when your app is running in portrait vs landscape mode. In your storyboard screenshot above you see the landscape-sized frame. The screen capture from your simulator shows the portrait-size frame. You'll need to set the struts and springs of your subviews (the UIPickerView, the brushed metal buttons, the white box below, etc) so that these elements resize (or not) and maintain their relative (or absolute) position in the parent view.
The easiest way to do this is to set the values in your storyboard, using the Size Inspector in the right column. Select which element you want to change settings for and then look for this:
By clicking on the red arrows inside the inner box you will toggle on/off the "springs", which determine whether your subview expands when the parent view expands, or whether it maintains its original size when that happens. By clicking on the outer red I-bars you will toggle on/off the "struts", which determine whether you subview will maintain a fixed distance from its parent view's edge when the parent view's size changes. Setting the right combination of these will make your view to look correct in both portrait and landscape orientations.
You can also change these settings programmatically in your code by setting the view's autoresizingMask property. See for reference:
http://developer.apple.com/library/ios/DOCUMENTATION/WindowsViews/Conceptual/ViewPG_iPhoneOS/CreatingViews/CreatingViews.html#//apple_ref/doc/uid/TP40009503-CH5-SW5

iPad Modal UIViewControllers position

How do I position this ViewController over the detail view modally? I want it to be right aligned so you can see the navigation portion greyed out.
[self.window addSubview:self.splitViewController.view];
MyModalViewController *modalvc= [MyModalViewController new]; //brevity
modalvc.modalInPopover = YES;
modalvc.modalPresentationStyle = UIModalPresentationStylePageSheet;
modalvc.view.autoresizeMask = UIViewAutoresizeFlexibleRightMargin;
[self.splitViewController presentModalView:modalvc animated:NO];
[self.window makeKeyAndVisible];
I also checked in MyModalViewController if im setting the views mask and I am not, nor is it getting magic values from a Nib.
Adjusting the frame before the present (using modalvc.view.frame) does nothing.
Adjusting the frame after the present seems to yield crazy results, and I only really need it to be half a width over in landscape... portrait is normal behavior.
edit
the picture confused people so I took it out, I dont want the modal view to be the size of the screen, I want to keep it UIModalPresentationStylePageSheet but have its ORIGIN moved right so that it covers the detail view portion in landscape
The modalPresentationStyle is what controls that. You've set it to UIModalPresentationStylePageSheet, which sets the height to the height of the screen, and the width to to the width of the screen in portrait orientation, exactly as in your screenshot.
I think the only way to get full width in landscape is to use UIModalPresentationFullScreen. See the UIViewController reference for more info.
I had a similar problem, and I ended up using a custom view controller that uses a background translucent view and a foreground opaque view that I am able to position anywhere I want by manipulating its frame. It's useful as a lightbox for videos and images.

Resources