Exclude navigation bar from iOS "safe area" - ios

In my iOS app, I have views rendered beneath the navigation bar. This is because the navigation bar is hidden until the user taps the screen.
The screenshots below illustrate my problem.
The "X" button is rendered beneath the iPhone X's notch and can hardly be seen. This is when I constrain the button's topAnchor to its superview!.topAnchor.
Note that this works as intended for all devices except the iPhone X.
The "X" button in this screenshot is anchored to its superview!.safeAreaLayoutGuide.topAnchor and renders below the navigation bar. This makes sense given Apple's documentation on safeAreaLayoutGuide:
"this guide reflects the portion of the view that is not covered by navigation bars, tab bars, toolbars, and other ancestor views."
However, I want the "X" button to render below the iPhone X's notch and underneath the navigation bar. Here's what it looks like when the navigation bar is hidden:
The "X" button should render right below the notch.
So my question is:
Is there a way to exclude the navigation bar from a view's safeAreaLayoutGuide? And if not, what are my options, other than manually offsetting the button on iPhone X's.
Note that I'm doing everything programmatically. I no longer use Storyboards.
Thanks!

You can change the view controllers safe area insets by adding additional insets.
Create a UIEdgeInsetsMake() object where you subtract the navigation bar height from the top inset. Then add it to the View Controllers additionalSafeAreaInsets.
Declaration: var additionalSafeAreaInsets: UIEdgeInsets { get set }

This is not an answer, but a workaround:
Keep a reference to the "X" button's top constraint.
In layoutSubviews(), update the constant of the top constraint depending on the "X"'s superview and window safeAreaInsets.
override func layoutSubviews() {
// To support devices with a "safe area". If the receiver has a top safe area, position
// the close button beneath the _window's_ safe area.
// Note that using the receiver's safe area does not work in this case because it includes
// the navigation bar. We want to render the close button beneath the navigation bar.
let windowSafeAreaInsets = UIApplication.shared.keyWindow!.safeAreaInsets
// Only use the safe area if the receiver _and_ window have a top safe area. This handles
// the case of non-safe area devices using a hidden navigation bar.
closeButtonTopConstraint.constant = safeAreaInsets.top > 0 && windowSafeAreaInsets != .zero
? windowSafeAreaInsets.top : 16
// Possibly do something similar for landscape and the "X" button's trailing constraint.
}

Related

UICollectionView under Navigation Bar, but not under Tab Bar

I have a UICollectionView that I want to go under the navigation bar. Basically I want it to ignore the entire top safe area, yet still want it to respect the bottom safe area, as there's a tabbar there. This is how it currently looks:
But I want the first cell to start directly the very top of the screen, under the (translucent) navigation bar and the status bar.
If I set collectionView.contentInsetAdjustmentBehavior = .never, then the top part works great, but then the bottom part of the collection view content is hidden by the tabbar - you can't scroll all the way to the bottom so to speak. So I have to manually add a bottom inset again? How do I get the height of the tabbar, including any bottom safe area on devices that have the home bar? Or is there a better way to tell the collectionview to ignore only the top area for its content inset adjustment?
You just need to set the bottom content inset of the collection view manually, after setting the adjustment behavior to .never.
The correct inset (including the tab bar and any home bar) can be found in safeAreaInsets.
collectionView.contentInsetAdjustmentBehavior = .never
collectionView.contentInset.bottom = collectionView.safeAreaInsets.bottom
You'll need to do this at a point when the safeAreaInsets have been set, such as viewDidLayoutSubviews.
As far as I remember, it used to be possible by simply adjusting the edgesForExtendedLayout property of the containing view controller, but that was phased out when safe areas were introduced in iOS 11 I believe.

Unable to put view above navigation bar

I am trying to design a side menu, whose height would be equal to screen height (therefore hiding the navigation bar too). However I am unable to get the same.
I have put constraints as this:
and in viewDidLayoutSubviews()
I have mentioned - sideMenuTopConstraint.constant = -1 * (self.navigationController?.navigationBar.frame.height)!
However I see no change. Also by increasing the height of the side menu view in storyboard, I see that it is always below the nav bar. How do I make it appear above it?
You need to add the view to window as a subview, that should bring this view above the navigation bar as needed.
This is because window is the root of all the views.
UIApplication.sharedApplication().keyWindow?.addSubview(desiredViewHere)

UIView overlapping with tab bar elements iOS

I made a view for a switch view. Now I already had tabbed menu buttons in it. The problem is the subview for the switch view seems to be overlapping with the tab bar icons at the bottom. Please help.
i think thats because your tabBar is set to translucent. set it to non-translucent from storyboard or programatically.
self.tabBarController.tabBar.translucent = false
You should have a constraint that sets the vertical space between your subview and your tabbar.
Also, check that your layout respects the iPhone X Safe Area (https://developer.apple.com/ios/human-interface-guidelines/overview/iphone-x/), from your image it seems that your elements overlap the safe area at the bottom.

How to remove white top space that appears on tab in others (UITabBarController)

I am working on Xamarin Studio with Mono but you can respond in Obj-C or Swift I don't mind. (Please don't mind the icons, design, translations, etc on the screenshots this is WIP)
I have this strange issue I can't resolve :
A white blank space appears under the top bar of my UIView which is in a UITabBarController, at first I thought it was the "Adjust Scroll View Insets" option enabled but it is not. I also tried to remove my UIWebView and try with a Label, same problem.
The blank space only appears on views that are "tabbed" in the "Others" section of my UITabBarController (when the screen is too small). You can see on my screenshots on an iPad the white space is not here, but on every other devices (iPhone, iPhone plus) it appears !
I can put a negative top constraint if the device is not an iPad but it is not the proper way to remove it ...
You can find the screenshots of (in order) :
The storyboard view
Top Constraint of the WebView
The whitespace on iPhone (but not on iPad and Storyboard, shouldn't be here !!)
StoryBoard structure
No whitespace on iPad (this is the normal behaviour, I want it on smaller devices too)
ViewController parameters
That by sight the white space is exactly the nav bar is a giveaway that in adjusting for those bars something is awry.
There is a contradiction in your layout that is probably the source of the issue. You have "extend edges" selected for the map view, but you have the top of the map view constrained to the top layout guide. The top layout guide is located at the bottom of the nav bar, so if something was adjusting insets for being under the bar, and knew the bar height and assumed it was under the bar, then this would result.
That wouldn't explain why it looks correct when included in the tab bar's initial view controllers, but it's possible when it appears from more pop-up, it is added to the view hierarchy so quickly it doesn't know the exact position of the top layout guide or the top bars, and so causes the glitch, whereas in the tab bar controller tabs it makes that adjustment on one of multiple layout passes.
Thank you for your answers, I found out what was that white space.
If I want to remove it I have to uncheck "Extent edges - Under Top Bars" and then do (or the NavigationBar will be gray, see Dark shadow on navigation bar during segue transition after upgrading to Xcode 5.1 and iOS 7.1):
NavigationController.NavigationBar.Translucent = false;
But trying to resolve the issue, I tried the following code :
ParentViewController.NavigationController.NavigationBar.Translucent = false;
And the white space turned into a second NavigationBar with tools to reorganize the tabs, and no need to uncheck "Extent edges - Under Top Bars" (especially if you don't set the tranlucent to false because the second navigation bar will go under it).
I will keep that second NavigationBar (https://developer.apple.com/reference/uikit/uitabbarcontroller/1621183-morenavigationcontroller), I like it. But for some reason it wasn't displayed until I set the ParentViewController NavigationController NavigationBar Translucent to false ... I don't really get why, it should have appeared directly but ...
Note that the proper way to display this NavigationBar if it is not displayed by default is to add the line in the UITabBarController class, and not in each ViewController ...
public partial class TabBarController : UITabBarController
{
public TabBarController(IntPtr handle) : base(handle)
{
}
public override void ViewDidLoad()
{
CustomizableViewControllers = null; //if you want to disable the edit button
NavigationController.NavigationBar.Translucent = false;
base.ViewDidLoad();
}
}
(Sorry for the french on the screenshots my device is in French ..)

Adjusting views when Status Bar hides on rotation

I've browsed around looking for an answer for this, but I've only found people with similar problems and not this exact problem, so hopefully someone here can help me!
I have an iPad App. On iPad, when you hold the iPad in either portrait or landscape, the status bar with the clock and battery is always shown. For this reason, I have some custom toolbars at the top of some of my View Controllers that account for those 20 points.
I am now working on making my App universal. The first thing I noticed is that when the iPhone is help in portrait mode the status bar is shown, but when it's held in landscape mode, the status bar hides and now I have a toolbar that is 20 points too tall.
I am not interested in either always hiding or always showing the status bar. I think the functionality of hiding it on landscape mode on the phone is fine. What I want to do is to be able to detect when the status bar is hidden so that I can adjust the Y position of my toolbar (so make it either 0 or -20). An even better solution would be if there's a way for me to set my layout constraints to handle this scenario.
The correct approach is:
Use a normal toolbar with a normal height - do not set any constraint on the height.
Add leading constraint 0 to superview (not superview margin), trailing constraint 0 to superview (not superview margin).
Add top constraint 0 to top layout guide (or top of safe area). This will appear to leave 20 pixels of space above the toolbar, but laugh an evil laugh and proceed.
Set the view controller as the toolbar's delegate (there is a delegate outlet for this purpose).
Have the view controller adopt UIBarPositioningDelegate and implement the delegate method as follows:
class ViewController: UIViewController, UIBarPositioningDelegate {
func position(for bar: UIBarPositioning) -> UIBarPosition {
return .topAttached
}
}
This will cause the apparent height of the toolbar to be extended up behind the status bar when there is a status bar, but it will have normal height and be smack against the top when there is no status bar.

Resources