TableView Showing Behind Tab Bar - ios

I am updating my app to use iOS 7 and I'm having a problem with a table view. My tab bar is translucent. The problem is when I scroll to the bottom of my table view, part of the last cell is still behind the tab bar. I'd like to have a bit of space between the last cell and the tab bar. I could fix this by using an opaque tab bar instead, but I want to keep it translucent.

Try setting
self.edgesForExtendedLayout = UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars = NO;
self.automaticallyAdjustsScrollViewInsets = NO;
Inside the tableview controller

Swift 4.x
let adjustForTabbarInsets: UIEdgeInsets = UIEdgeInsetsMake(0, 0, self.tabBarController!.tabBar.frame.height, 0)
self.yourTableView.contentInset = adjustForTabbarInsets
self.yourTableView.scrollIndicatorInsets = adjustForTabbarInsets

Check the screen shot
Check the under top Bar and Un-checke under Bottom Bar

SWIFT 3
put this inside viewDidLoad of your tableViewController:
self.edgesForExtendedLayout = UIRectEdge()
self.extendedLayoutIncludesOpaqueBars = false
self.automaticallyAdjustsScrollViewInsets = false

Swift 3.0
This is what worked for me. In your Custom ViewController:
override func viewDidLoad() {
super.viewDidLoad()
let adjustForTabbarInsets: UIEdgeInsets = UIEdgeInsetsMake(self.tabBarController!.tabBar.frame.height, 0, 0, 0);
//Where tableview is the IBOutlet for your storyboard tableview.
self.tableView.contentInset = adjustForTabbarInsets;
self.tableView.scrollIndicatorInsets = adjustForTabbarInsets;
}

Not to sure I like the solution but it works for me.
With iOS 11 I have no issue, I simply use the following in viewDidLoad():
self.collectionView.bottomAnchor.constraint(self.view.safeAreaLayoutGuide.bottomAnchor).isActive = true
However on iOS 10 I need to hack my way like this:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let tabBarHeight: CGFloat = (self.parent?.tabBarController?.tabBar.frame.size.height)!
if #available(iOS 11.0, *) {
} else {
self.collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -tabBarHeight).isActive = true
}
}

This is working for me
override func viewDidLoad() {
self.edgesForExtendedLayout = UIRectEdge()
self.extendedLayoutIncludesOpaqueBars = false
}

If any view shows behind a UITabBar you can grab the bottomLayoutGuide and make adjustments at runtime. What I do is have a BaseViewController that all my view controllers inherit from. Then if the tab bar is visible we adjust the view like so:
import UIKit
class BaseVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidLayoutSubviews() {
//Ensures that views are not underneath the tab bar
if tabBarController?.tabBar.hidden == false {
var viewBounds = self.view.bounds;
var bottomBarOffset = self.bottomLayoutGuide.length;
self.view.frame = CGRectMake(0, 0, viewBounds.width, viewBounds.height - bottomBarOffset)
}
}
}
Since I don't use storyboards (where you can click a checkbox in IB to fix this problem), this has been the best solution I have found.

It is really hard to resolve the issue without detail information or actual codes. I have similar issue of tabview behind UItabBar in my project. The solutions offered here do not work in my case. After exploring my codes, I found a solution for my case.
Here is brief explanation of my case. I have a UItabBar in main view with two tab buttons. In one tab view, there is table view. If user taps on a row, a detail view is presented by using navigation controller. In the detail view, the tab bar is hidden, and a toolbar is showing at the bottom.
In order to bring tab bar back and hide the toolbar when the main view is brought back, I have to explicitly show tab bar and hide toolbar in the event of viewWillAppear:
class myMainViewController: UITableViewController {
private var tabBarHidden: Bool? = {
didSet {
self.tabBarController?.tabBar.isHidden = tabBarIsHidden ?? true
}
}
private var toolBarIsHidden: Bool? {
didSet {
let hidden = toolBarIsHidden ?? true
self.navigationController?.toolbar.isHidden = hidden
self.navigationController?.setToolbarHidden(hidden, animated: true)
}
}
...
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarIsHidden = false
self.toolBarIsHidden = true
}
...
}
I finally realize that the visibility of bar at the bottom is set in the event of viewWillAppear. At that time, the tableView or scroll view's content insets are set already based on no bar at the bottom. That's why my tableView is behind the bottom bar.
The solution I found is to reset content insets in the event of viewDidAppear:
override func viewDidAppear(_ animated: Bool) {
// In the event of viewWillAppear, visibilities of tool bar and tab bar are set or changed,
// The following codes resets scroll view's content insets for tableview
let topInset = self.navigationController!.navigationBar.frame.origin.y +
self.navigationController!.navigationBar.frame.height
let adjustForTabbarInsets: UIEdgeInsets = UIEdgeInsetsMake(
topInset, 0,
self.tabBarController!.tabBar.frame.height, 0)
self.tableView.contentInset = adjustForTabbarInsets
self.tableView.scrollIndicatorInsets = adjustForTabbarInsets
}

The best approch would be to Embed TabBarController to your ViewController (Editor -> Embed In -> TabBar Controller)and set the bottom of the tableview to be bottom of safe area of viewcontroller. The other ways wont be as perfect as this one.

You need to adjust the height of the table view. Just leave 49px at the bottom, as the tabbar height is 49 px. Adjust the height of table view so that it leaves 49px space below it.

Related

iOS 13 remove UIView for simulate status bar background

I'm working with a UITableViewController which when scrolling makes the navigationBar disappear. Now when the navigation bar is hidden when the user swipes the table view the contents of the cells are seen below the status bar ...
To solve this problem I tried to insert a UIView to simulate a background of the status bar and everything works but the problem is that when I close the UITableViewController the background view of the status bar is not removed from the superview
For now my code is this, can you help me understand where I am wrong? why can't I remove the UIView from the superview?
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
setupStatusBarView()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.navigationBar.isHidden = true
UIApplication.shared.windows.first?.viewWithTag(1)?.removeFromSuperview()
}
//MARK: - Setup Status Bar View
func setupStatusBarView() {
let height = view.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
let statusBarView = UIView()
statusBarView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height:height+5)
statusBarView.backgroundColor = .systemBackground
statusBarView.tag = 1
UIApplication.shared.windows.first?.addSubview(statusBarView)
}
viewDidLayoutSubviews get calls multiple times and you have put setupStatusBarView() in viewDidLayoutSubviews that means your background view has been added multiple times and this is totally wrong flow!
You are removing topmost view only not previous ones!
You should set frame in viewDidLayoutSubviews and should add the view from viewDidLoad!
try this one
let subviewArray = UIApplication.shared.windows.first?.subviews
for view in subviewArray!{
if view.tag == 1{
view.removeFromSuperview()
}
}

why UITabbarController is going too bottom

I have Implemented top items a view with UICollectionview and UIPagecontroller to get android like pagetabs.
For each menu there is a container ViewController but in one of them need UITabBarController.
Why UITabBarController is going down.
I have tried moving menuVIew(Contacts,Recents,etc) up but still did not work.
For the top view, use UISegmentedControl, and make background color, same as border color.
For the tab bar, you mean that it is covered by home indicator?
Turn on Use Safe Area Layout Guides, for this UITabController, and you should be good to go.
let tabBar = UITabBar()
override func viewDidLoad() {
super.viewDidLoad()
addTabbar()
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
addHeightConstraintToTabbar()
}
func addTabbar() -> Void {
self.view.addSubview(tabBar)
tabBar.translatesAutoresizingMaskIntoConstraints = false
tabBar.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
tabBar.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
tabBar.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
let item1 = UITabBarItem(tabBarSystemItem: UITabBarSystemItem.bookmarks, tag: 1)
let item2 = UITabBarItem(tabBarSystemItem: UITabBarSystemItem.contacts, tag: 2)
tabBar.items = [item1, item2]
self.view.bringSubview(toFront: tabBar)
}
func addHeightConstraintToTabbar() -> Void {
let heightConstant:CGFloat = self.view.safeAreaInsets.bottom + 49.0
tabBar.heightAnchor.constraint(equalToConstant: heightConstant).isActive = true
}
Result:
I have faced this issue before for iPhone X. I found a weird solution of this as below:
Change the bottom constraint from TabBar to Safe Area not the Super view and change the constraint value to 1 as shown in the below image.
This works on both The normal devices and devices with notch display. Check out the screenshots :
Hope this helps you.

UISplitViewController: titleView in DetailViewController disappears on landscape orientation, intended behaviour?

I'm adding a custom titleView inside a navigation bar using navigationItem.titleView for both Master/Detail VC. On changing the orientation of the device to landscape, titleView under MasterViewController works fine, but for DetailViewController titleView disappears. On changing the orientation back to portrait titleView appears back for DetailViewController. I have also attached a link for source code and video.
Is this an intended behavior or am I making a mistake from my side or is it an issue from Apple's side ?
//Custom Title View:
class TitleView: UIView {
override func sizeThatFits(_ size: CGSize) -> CGSize {
return CGSize(width: 50, height: 20)
}
}
class DetailViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Adding titleView for Master/Detail VC:
navigationItem.titleView = {
//Setting frame size here, did not make any difference
let view = TitleView(frame: .zero)
view.backgroundColor = .red
return view
}()
}
}
Full source code here: https://github.com/avitron01/SplitViewControllerIssue/tree/master/MathMonsters
Video highlighting the issue:
https://vimeo.com/336288580
I had the same issue. It seems an iOS bug. My workaround was to reassign the title view on every view layout. I used this piece of code in my DetailViewController:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if let v = navigationItem.titleView {
navigationItem.titleView = nil
navigationItem.titleView = v
}
}
For those who stumble upon this, see also iOS 11 navigationItem.titleView Width Not Set. Basically, there's two suggested workarounds:
use a custom UIView that tells iOS to treat its intrinsicContentSize to be as big as possible with UIView.layoutFittingExpandedSize
use widthAnchor/heightAnchor constraints to set width and height of your view

UINavigation with UITextField as subview

I want to make UI like following,
I tried to increase the height of UINavigationController like
[self.navigationController.navigationBar setFrame:CGRectMake(0, 0, 320, 110)];
but navigation title and BarButtonItem appears down.
i can not add hide navigation as i am using third party side menu which uses Left BarButtonItem.
i can very well keep navigation as it is and below i have added uiview as subview with same background colour as that of navigation but there is fine line appears in between...:(
You can't increase height of UINavigationBar. Add an UIView with those elements just below the UINavigationBar.
Then there will be extra line below UINavigationBar. You can remove that line. Refer How to hide iOS7 UINavigationBar 1px bottom line
or else use this code(I have just copied it from that link in swift, convert it to Objective-C)
import UIKit
class ViewController: UIViewController {
private var shadowImageView: UIImageView?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if shadowImageView == nil {
shadowImageView = findShadowImage(under: navigationController!.navigationBar)
}
shadowImageView?.isHidden = true
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
shadowImageView?.isHidden = false
}
private func findShadowImage(under view: UIView) -> UIImageView? {
if view is UIImageView && view.bounds.size.height <= 1 {
return (view as! UIImageView)
}
for subview in view.subviews {
if let imageView = findShadowImage(under: subview) {
return imageView
}
}
return nil
}
}
This is the result
Actually you cant change height of UINavigationBar. Your actual ViewController's view will start from 64 pixel from TopLayoutGuide (Vertical Space)
Status Bar Height is 20 Pixel
Navigation Bar Height is 44 Pixel
So to design such UI, simply do these steps
Hide NavigationBar on that view controller like
self.navigationController.navigationBarHidden = YES;
self.navigationItem.hidesBackButton = YES;
Then add View from storyboard with vertical space as 0 from TopLayoutGuide. And design it as per your requirement. So you can
simply give this look and handle it using IBOutlets and IBActions
for each item
Also you can add UILable to show title of screen.
You can't increase the size of the UINavigationBar. That is of standard size 44 (Nav Height) + 20 (size of the status bar). Here you've two options to achieve.
First, by adding menu, dashboard title label & notification icon to the UINavigationBar. Add Jobs, Conditions, and Interview as a separate view below to navigation bar.
Second by customizing the entire view, as mentioned by #Pushkraj i.e, by hiding the current Navigation Bar and adding your own view which contains all the UI elements.

Hiding the tabbar and removing the space

Is there a way to hide tabbar and remove that space left (around 50px) ?
I tried
self.tabBarController?.tabBar.hidden = true
self.extendedLayoutIncludesOpaqueBars = true
No luck. I see blank space.
If you're still seeing a black stripe under your hidden tab bar, have you tried to select Extend Edges Under Opaque Bars here?
Make also sure that Under Bottom Bars is still selected. Hope it helps!
Swift 3:
extension UITabBarController {
func setTabBarVisible(visible:Bool, duration: TimeInterval, animated:Bool) {
if (tabBarIsVisible() == visible) { return }
let frame = self.tabBar.frame
let height = frame.size.height
let offsetY = (visible ? -height : height)
// animation
UIViewPropertyAnimator(duration: duration, curve: .linear) {
self.tabBar.frame.offsetBy(dx:0, dy:offsetY)
self.view.frame = CGRect(x:0,y:0,width: self.view.frame.width, height: self.view.frame.height + offsetY)
self.view.setNeedsDisplay()
self.view.layoutIfNeeded()
}.startAnimation()
}
func tabBarIsVisible() ->Bool {
return self.tabBar.frame.origin.y < UIScreen.main.bounds.height
}
}
To use (if for example self is a UITabBarController):
self.setTabBarVisible(visible: false, duration: 0.3, animated: true)
Swift 2.x:
extension UITabBarController {
func setTabBarVisible(visible:Bool, duration: NSTimeInterval, animated:Bool) {
if (tabBarIsVisible() == visible) { return }
let frame = self.tabBar.frame
let height = frame.size.height
let offsetY = (visible ? -height : height)
// animation
UIView.animateWithDuration(animated ? duration : 0.0) {
self.tabBar.frame = CGRectOffset(frame, 0, offsetY)
self.view.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height + offsetY)
self.view.setNeedsDisplay()
self.view.layoutIfNeeded()
}
}
func tabBarIsVisible() ->Bool {
return self.tabBar.frame.origin.y < UIScreen.mainScreen().bounds.height
}
}
To use:
self.tabBarController?.setTabBarVisible(visible: false, duration: 0.3, animated: true)
After saw your screenshot in comment. I think you can try to set hidesBottomBarWhenPushed to true.
hidesBottomBarWhenPushed = true
Or storyboard.
It will hide bottom bar automatically when you pushed to another view controller, and appear it again when you go back.
Programmatically, add this to the next view controller for swift 4.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tabBarController?.tabBar.isHidden = true
edgesForExtendedLayout = UIRectEdge.bottom
extendedLayoutIncludesOpaqueBars = true
}
And add a background color
NOTE - This solution is to just to remove white space left after hiding tab bar.
For hiding tab bar best solution is - #Michael Campsall answer here
The simplest solution to this is to change your view's(in my case its tableView) bottom constraints, instead of giving bottom constraints with BottomLayoutGuide give it with superview. Screenshots attached for reference.
Constraints shown in below screenshots creates the problem, change it according to next screenshot.
Actual constraints to remove white space should be according to this(below) screenshot.
For those that like to do everything programmatically, add this line to the init method of a ViewController that shouldn't have the tabBar:
hidesBottomBarWhenPushed = true
I was facing the same issue and root cause was BOTTOM CONSTRAINT
Make sure you set the bottom constraint of your bottom most view in the main view hierarchy with SUPERVIEW, NOT "SAFE AREA"
Hope this helps someone..
The third answer on this question works for me in the following way:
The code on my view controller
#IBAction func buttonPressed(sender: AnyObject) {
setTabBarVisible(!tabBarIsVisible(), animated: true)
}
func setTabBarVisible(visible: Bool, animated: Bool) {
// hide tab bar
let frame = self.tabBarController?.tabBar.frame
let height = frame?.size.height
var offsetY = (visible ? -height! : height)
print ("offsetY = \(offsetY)")
// zero duration means no animation
let duration:NSTimeInterval = (animated ? 0.3 : 0.0)
// animate tabBar
if frame != nil {
UIView.animateWithDuration(duration) {
self.tabBarController?.tabBar.frame = CGRectOffset(frame!, 0, offsetY!)
self.view.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height + offsetY!)
self.view.setNeedsDisplay()
self.view.layoutIfNeeded()
return
}
}
}
func tabBarIsVisible() -> Bool {
return self.tabBarController?.tabBar.frame.origin.y < UIScreen.mainScreen().bounds.height
}
In storyboard:
The view controller main view background color is black color:
Then you could have another view inside (background color white), constrained trailing and leading space to superview and top and bottom space to the layout guide.
And the result is:
My preferred way to do that is using a wrapping controller. If I want to hide the tab bar, I just increase the height of the tab bar controller, thus effectively the tab bar is moved out of the screen.
With this solution you don't need to hack tab bar frame and you don't depend on navigation controller push animation:
import UIKit
class ViewController: UIViewController {
let tabController: UITabBarController = {
let tabController = UITabBarController()
// setup your tabbar controller here
return tabController;
}()
var tabbarHidden = false {
didSet {
var frame = self.view.bounds;
if (tabbarHidden) {
frame.size.height += self.tabController.tabBar.bounds.size.height;
}
self.tabController.view.frame = frame;
}
}
override func viewDidLoad() {
super.viewDidLoad()
// add the tab controller as child controller
addChildViewController(self.tabController)
self.tabController.view.frame = self.view.bounds
self.tabController.view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
self.view.addSubview(self.tabController.view)
self.tabController.didMoveToParentViewController(self)
// for debugging
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(switchTabbar))
self.tabController.view.addGestureRecognizer(tapRecognizer)
}
override func childViewControllerForStatusBarStyle() -> UIViewController? {
return self.tabController
}
override func childViewControllerForStatusBarHidden() -> UIViewController? {
return self.tabController
}
func switchTabbar() {
UIView.animateWithDuration(0.3) {
self.tabbarHidden = !self.tabbarHidden
}
}
}
try to set the tab bar translucent to before you hide the tab bar set to false again when you want to show again.
it works for me.
tabBarController?.tabBar.isTranslucent = true
Yes. You can hide your tab bar when you push to view controller. You can show tab bar in your home. You can hide your tab bar when you push to next View controller.
See the Hide Botton Bar on Push following image and set in all viewcontrollers where you dont want tab bar.
Hope it helps..
Sometimes that easiest way is just to add a view that uses the UIScreen bounds.
let whiteView = UIView()
whiteView.backgroundColor = .white
view.addSubview(whiteView)
whiteView.translatesAutoresizingMaskIntoConstraints = false
whiteView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
whiteView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
whiteView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
whiteView.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height).isActive = true
Cause sometimes the view edges extends beyond the nav bar giving you new problems if you extend the view layout.
Tested in Swift 5.4.
If you're adding any ViewController's view as subview programmatically and not using pushViewController, then you can simply try as follows:
// When you wanna hide TabBar
tabBarController?.tabBar.isHidden = true
tabBarController?.tabBar.isTranslucent = true // This is the key point!
// When you wanna show TabBar
tabBarController?.tabBar.isHidden = false
tabBarController?.tabBar.isTranslucent = false // This is the key point!
This code works on iOS 10, 11, and iPhone X (including simulators) to show/hide the tabBar. I created it several years (iOS 7 time frame?) and it has worked reliably since that time.
It works great on iPhone X as long as content content in your childViewControllers (in tabs) is pinned to topLayoutGuide, bottomLayoutGuide or SafeArea and not the main views walls. Then it all just works. Enjoy!
#interface UITabBarController (HideTabBar)
#property (nonatomic, getter=isTabBarHidden) BOOL tabBarHidden;
-(void)setTabBarHidden:(BOOL)hidden animated:(BOOL)animated;
#end
#implementation UITabBarController (HideTabBar)
-(BOOL)isTabBarHidden
{
CGRect viewFrame = self.view.frame;
CGRect tabBarFrame = self.tabBar.frame;
return tabBarFrame.origin.y >= viewFrame.size.height;
}
-(void)setTabBarHidden:(BOOL)hidden
{
[self setTabBarHidden:hidden animated:NO];
}
-(void)setTabBarHidden:(BOOL)hidden animated:(BOOL)animated
{
BOOL isHidden = self.tabBarHidden;
if(hidden == isHidden)return;
UIView *transitionView = [[[self.view.subviews reverseObjectEnumerator] allObjects] lastObject];
if(transitionView == nil) {
NSLog(#"UITabBarCategory can't get the container view");
return;
}
CGRect viewFrame = self.view.bounds;
CGRect tabBarFrame = self.tabBar.frame;
CGRect containerFrame = transitionView.frame;
CGRect selectedVCFrame = containerFrame;
tabBarFrame.origin.y = viewFrame.size.height - (hidden ? 0 : tabBarFrame.size.height);
containerFrame.size.height = viewFrame.size.height - (hidden ? 0 : tabBarFrame.size.height);
if([self.moreNavigationController.viewControllers containsObject:self.selectedViewController]) {
selectedVCFrame = self.selectedViewController.view.frame;
selectedVCFrame.size.height += hidden ? tabBarFrame.size.height : -tabBarFrame.size.height;
}
self.selectedViewController.view.frame = selectedVCFrame;
[UIView animateWithDuration:.5 animations:^{
self.tabBar.frame = tabBarFrame;
transitionView.frame = containerFrame;
[self.selectedViewController.view setNeedsLayout];
}];
}
#end
Usage - I call it in the viewController on rotation events like so:
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
// Hide TabBar on iPhone, iPod Touch
if([UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPad) {
if(_startDateEditor.editing) return;
if(fromInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown || fromInterfaceOrientation == UIInterfaceOrientationPortrait)
[self.tabBarController setTabBarHidden:YES animated:YES];
else
[self.tabBarController setTabBarHidden:NO animated:YES];
}
}
For me in iOS 13 I had to display image in cell with full screen, I had collection view with trailing, leading, top, bottom constraint. I removed all constraint. set collection view frame to UIScreen.main.bounds. then return sizeForItemAt as collection frame size.

Resources