I am currently trying to create a temporary view that covers the entire window of my app (except the tab bar) while the background threads loads the content. The way I am doing this is the following
super.viewDidLoad()
let window = UIApplication.shared.keyWindow!
let v = UIView(frame: window.bounds)
window.addSubview(v)
However, this creates a view that covers the tab bar. Is there a way to have a view cover the entire screen except the tab bar?
Yes, if you are in a view controller where there is a tab bar (i.e. inside a UITabBarController interface), the top of the tab bar is the bottom of the safe area, so instead of setting the frame to window.bounds, adjust the height of the frame in accordance with where the bottom of the safe area insets are.
Related
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.
I am using the tab bar based app and on detail screen the tab bar is hidden. The issue is when the tab bar is hidden it will still occupy the white space that of tab bar and safeAreaLayoutInsets are not updated. On orientation change or moving from background to foreground it will work.
self.tabBarController.tabBar.hidden = YES;
View hierarchy
UITabbarController
|--UISplitViewController
|--UIViewController (first VC)
|--UINavigationController
|--UIViewController (second VC)
The issue is similar to one reported in Apple Forum
If you need to toggle the tab bar visibility of a visible view, this workaround fixes the layout:
let currentFrame = tabBarController.view.frame
tabBarController.view.frame = currentFrame.insetBy(dx: 0, dy: 1)
tabBarController.view.frame = currentFrame
This code should be executed immediately after the tab bar visibility is changed. It triggers an update of the safe area and a single layout pass of the view. The resizing of the frame is not visible to the user.
It is a workaround and certainly not great, but it works for us and does not seem to have negative side effects. Moreover, I do not expect negative side effects in the future when iOS updates the layout by itself.
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)
I have a project where our tab bar has a big middle button (that extends above the tab bar) and other custom behaviors including a badge icon and colored labels.
I got "smart" and decided to just write my own tab bar and tab bar controller to go with it. The problem I've run into is that when one of the tabs is wrapped in a UINavigationController, that view always takes up the whole screen (you can't capture a UINavigationController into a small subview) and so I have to manually inset the content on those views.
Is there a smart way to handle this? It feels gross to just cut the content short on each screen by 100 points...that doesn't feel right at all.
What approach should I take...or should I just automate the content insets programmatically?
A tab bar controller is just a scroll view with a view at the bottom that toggles between the scroll view's offset. I assume you want the tab bar controller to be at the root of your app, so in the root view controller, add a UIScrollView.
Then add the views of the view controllers (the tabs) to that scroll view, and anchor them appropriately so that the scroll view scrolls. Make the heights and widths of these view controllers full screen. Before you add them to the scroll view, you must create a parent-child relationship between the root view controller and its tabs.
self.addChildViewController(tabOneViewController)
tabOneViewController.view.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(tabOneViewController.view)
tabOneViewController.didMove(toParentViewController: self)
tabOneViewController.delegate = self // so that your tabs can communicate back to the controller
// add constraints
Each of these view controllers will ideally be or contain the navigation controller for that section.
Then just add the tab bar to the view of the view controller, not to the scroll view (add this after the scroll view so that it sits above the scroll view). This tab bar is just a regular UIView, most often anchored to the view controller's bottom safe area. Because its a part of the view controller's view, and not the scroll view, it has no impact on the content behind it.
The benefit of a custom tab bar setup like this is that you can navigate between tabs on tap or by pan gesture. To navigate between tabs by tapping on the buttons in the tab bar, simply change the scroll view's content offset:
// this would move to the third tab
scrollView.contentOffset = CGPoint(x: view.bounds.width * 2, y: 0)
Add your bells and whistles and you're set.
I had an UIPageViewController embedded in a NavigationController embedded an TabBarController.
I supposed every child view of the UIPageViewController fits the size within the UITabBarViewController.
The first child view looks fine:
Switch to the next (vertically), it's view suddenly resizes and the view length expands over the bottom bar:
Actually it's not under the bottom bar but clipped to that size (which means if you pull up the view you still cannot see the whole but the cut text).
I did unchecked every related view controller's Under Bottom Bar & Adjust Scroll View Inset but nothing works.
Any suggestion would be appreciated.
Try this in table view controller viewDidLoad() method.
self.extendedLayoutIncludesOpaqueBars = NO;
OR
you can set property in Interface Builder:
Uncheck Extend Edges: Under Bottom Bars, Under Opaque Bars.