UITableView is under the TabBar - ios

I have a custom UITableView with custom cells (70px height for each cell).
I have also a 49px UITabBar, but it's hides the tableView.
I've tried to add this line to the TableView awakeFromNib method but it didn't work:
self.commentsTableView.contentInset = UIEdgeInsetsMake(0, 0, 49, 0)
Any idea how can I solve this?
Thanks!

i don't know what you did exactly, but try like this:
self.edgesForExtendedLayout = UIRectEdgeAll;
self.tableview.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, CGRectGetHeight(self.tabBarController.tabBar.frame), 0.0f);
I hope, this will work.

I ran into this issue when dealing with a table view in a navigation controller that did not have translucent bars. I performed a setup similar to the following:
override func viewDidLoad() {
super.viewDidLoad()
// Without this there is some extra fast inertia when slowly
// scrolling to the top.
extendedLayoutIncludesOpaqueBars = true
// Don't extend the tableview past the bottom bar, though.
// If we do then a tab bar or bottom nav bar will block
// content.
edgesForExtendedLayout = [.top, .left, .right]
}
However, I later discovered that a couple of checkboxes were unchecked in a storyboard higher up the hierarchy. Specifically these two:
Checking these two boxes removed the need to care about the content insets and the layout extending behavior in that view controller

This did the trick for me in Swift 3.
if let tabBarController = tabBarController {
self.tableView.contentInset = UIEdgeInsetsMake(0.0, 0.0, tabBarController.tabBar.frame.height, 0.0);
}

You should config the corresponding view controller with following code to remove the edges extend (It defaults to UIRectEdgeAll)
edgesForExtendedLayout = []

Try this
self.commentsTableView.contentInset = UIEdgeInsetsMake(49, 0, 0, 0)
self.commentsTableView.setContentOffset(CGPoint.init(x: 0, y: -49), animated: false)

If you changed self.hidesBottomBarWhenPushed=true in previous view controller, then make sure to change it to self.hidesBottomBarWhenPushed=false inside override func prepare(for segue: UIStoryboardSegue, sender: Any?) {}

Try to use constraints for tableView and TabBar like:

Related

How do I prevent UITabBarController from updating subviews?

Inside of a ContainerView, I have a UITabBarController subclass where I have modified the UITabBar Y Position. Instead of it being on the bottom, I have moved it closer to the top using this code:
override func viewDidLayoutSubviews() {
tabBar.frame = CGRect(x: 0, y: 0, width: tabBar.frame.size.width, height: tabBar.frame.size.height)
super.viewDidLayoutSubviews()
delegate = self
selectedViewController?.view.frame.origin = CGPoint(x: 0, y: tabBar.frame.size.height)
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
selectedViewController?.view.frame.origin = CGPoint(x: 0, y: tabBar.frame.size.height)
}
It works great initially: the first tab view contains a UITableView, and it seems to adjust its frame for the new position of the UITabBar. However, if I push a ViewController on the stack, halfway through the push animation, the bottom of the view containing the UITableView jumps up like the UITabBar is still there. So, a portion of the top of the view ends up behind the UITabBar and upon popping this new ViewController off the stack, back to the UITabBarController, the view(with the tableView) remains in this position behind the new position of the tabBar. I've tried changing the view origin in ViewWillAppear, ViewWillDisappear, ViewDidDisappear, and none of these methods were able to reset the position of the view(containing the tableView) to beneath the new location of the UITabBar.
I'm not sure what's causing this to happen, or what lifecycle method to investigate.
Any ideas or suggestions?
EDIT: The TableView is the one actually changing it's Origin, NOT the view.
Seems like you might need to put the super.viewDidLayoutSubviews() call at the top of your override. Have you tried that? I would think that the superclass method actually also adjusts the position so your change might be getting overwritten.
I solved this layout issue by executing this code in both ViewWillAppear & ViewWillDisappear. I cannot explain why this works, but I do know that it does.
tabBar.invalidateIntrinsicContentSize()
tabBar.superview?.setNeedsLayout()
tabBar.superview?.layoutSubviews()

How to set NavigationBar overlapping the ViewController content in iOS11

I want navigationBar to overlap the root view of view controller, I have tried two methods:
self.additionalSafeAreaInsets = UIEdgeInsetsMake(-100, 0, 0, 0);
and
self.edgesForExtendedLayout = UIRectEdgeAll;
self.extendedLayoutIncludesOpaqueBars = YES;
Neither of these options work.
So at the moment it looks like the figure below (The gradient orange is the navigationBar)View content below the navigationBar
However what I'd like to achieve is that the actual content of the viewController starts at the top of the screen and the gradient orange overlap the viewController.
If I still can not express it clearly, please tell me. Thank you.
Not sure what u meant but maybe:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(false, animated: false)
}
Or set it visible from the Storyboard

Tab Bar hides CustomView containing CollectionViewController

I have created a separate customView class containing collection view along with its Xib, and then i try to load this customView to one of the controller's view which is connected to a Tab Bar Controller. The view gets loaded perfectly but the last item of collection view is hidden behind the tab bar.
It would be great to have a solution for that.
My customView looks as
Try this
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let bottomOffset: CGFloat = (tabBarController?.tabBar.frame.height)! // this your tabbar height you can replace with static number eg. 44
collectionView?.contentInset = UIEdgeInsetsMake(0, 0, bottomOffset, 0)
}
Try to uncheck Under Bottom Bars..
It worked for me.
set
self.edgesForExtendedLayout = UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars = YES;
self.automaticallyAdjustsScrollViewInsets = NO;

iOS 11 prefersLargeTitles not updating until scroll

I implemented a basic UIViewController with a UITableView that's wrapped in a UINavigationController. I set prefersLargeTitles to true:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.title = "Coffees"
}
However, the title stays small until I scroll the view, at which point it enlarges. I tried moving that call to where I create the UINavigationController, but there was no effect. I am sure the navigationController is not nil when I set prefersLargeTitles.
Should I be updating that property elsewhere? Or should I file a Radar?
Update:
This only seems to happen if my view contains a UITableView or is itself a UITableViewController
I recently hit the same issue and none of the suggestions worked for me. Instead, all I needed to do was to invoke sizeToFit(). Sample code:
private func configureNavigator() {
guard let navigationController = navigationController else { return }
navigationController.navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .automatic
navigationController.navigationBar.sizeToFit()
}
I hope this helps!
For me the only working solution is:
DispatchQueue.main.async { [weak self] in
self?.navigationController?.navigationBar.sizeToFit()
}
in
viewWillAppear()
I had the same issue only on one tableview ...
I had to set :
self.tableView.contentInsetAdjustmentBehavior = .never
so that my tableview stop scrolling when uiviewcontroller was loaded.
It's the tableview automatic scrolling that makes the large title being hidden
Hope this helps
I had the same problem. Although you are not using Storyboards but I hope this could help someone. I checked "Prefer Large Titles" for the Navigation Controller (not the View Controller) I embedded my TableViewController in. All the View Controllers after the Navigation Controller turned and had large titles, and it should work.
Same issue with Swift 5.2
my view contains tableView and prefersLargeTitles is not updating until scroll, I fixed it by setting
self.tableView.contentInsetAdjustmentBehavior = .never
Modifying the contentInset of the tableView with top:1 will force the NavigationBar to expand and display the large titles.
Obj-C
-(void) viewWillAppear:(BOOL)animated {
if (#available(iOS 11.0, *)) {
tableView.contentInset = UIEdgeInsetsMake(1, 0, 0, 0);
}
}
Swift
override func viewWillAppear(_ animated: Bool) {
if #available(iOS 11.0, *) {
tableView.contentInset = UIEdgeInsetsMake(1, 0, 0, 0)
}
}
Note: If you have a tableView.reloadData() in your viewWillAppear make sure to call it after editing the contentInset
In my case the solution was to set tableView's top align to Safe Area and not Superview
I ran into the same issue and found that it’s usually best to set the prefersLargeTitles property from the view controller or object that sets it up, and to do so before it is presented.
For instance, if the view controller in question is shown upon app launch:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let window = UIWindow(frame: UIScreen.main.bounds)
let someViewController: UIViewController = CustomViewController()
let theNavController = UINavigationController(rootViewController: someViewController)
theNavController.navigationBar.prefersLargeTitles = true
window.rootViewController = theNavController
window.makeKeyAndVisible()
return true
}
or if presenting a particular view controller:
let someViewController: UIViewController = CustomViewController()
let theNavController = UINavigationController(rootViewController: someViewController)
theNavController.navigationBar.prefersLargeTitles = true
present(theNavController, animated: true, completion: nil)
I found this method to be a more sure-fire way to ensure that the navigation title is displayed accordingly. Hope this helps! :)
I have wasted some considerable amount of time on this as prefersLargeTitle saga works on some view controllers as expected and with some it produces the same issue above.
Solution for me was to uncheck Extended Edges Under Top Bars in IB - for those view controllers who show large title momentarily until the contents of the table view are loaded then navigation bar jumps back up to regular size. It only shows the large title when scrolling the table view down.
This is backward compatible with iOS 10 and does not leave any empty space above the first row in the table view.
I had checked prefersLargeTitle on the navigation controllers attributes inspector only in IB - nothing in code. Same for largeTitleDisplayMode = .always
As for why this happens with some view controllers and not others, I have absolutely no idea!
In the storyboard I set the Navigation Item's Large Title to Never.
In my ViewController's viewDidLoad method I set the following:
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .always
Programmatically:
In AppDelegate.swift:
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
let navigationController = UINavigationController.init(rootViewController: ViewController())
window?.rootViewController = navigationController
In ViewController:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .automatic
}
override func loadView() {
super.loadView()
view.addSubview(tableView)
view.addSubview(loadingView)
NSLayoutConstraint.activate([
tableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
tableView.widthAnchor.constraint(equalTo: view.safeAreaLayoutGuide.widthAnchor),
tableView.heightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.heightAnchor)
])
}
Make sure your tableView has beed previously added to your view.
I tried all of the other answers but what worked for me was to set the content offset of my UITableView to CGPoint(x: 0, y: -1) in viewDidLoad:
tableView.setContentOffset(CGPoint(x: 0, y: -1), animated: false)
I just had this same issue and, in my case, it turns out that the Storyboard structure that was working in iOS 10 with Swift 3 (and also works with iOS 11 with Swift 3) was causing the issue on iOS 11 with Swift 4.
To elaborate:
I had a regular UIViewController in my storyboard that I had set to a UINavigationController subclass (my hierarchy is similar to yours, with UITabBarController subclass → UINavigationController subclass → UITableViewController subclass).
In iOS 10, this worked fine.
In iOS 11, this also works fine when you run the existing Swift 3 app.
However, with the Swift 4 app, running on iOS 11, I was seeing the same symptoms you described (large titles only appear when you pull/scroll the view down).
To fix, I replaced the UIViewController-based elements in the Storyboard with actual UINavigationController instances (which contain a UINavigationBar explicitly in the Storyboard – I have a hunch this is where the crux of the issue stems from, as the UIViewController instances didn’t have that element explicitly declared within the Storyboard).
Anyway, that fixed the issue for me.
I’ll file radar as this looks like a Swift 4-based regression as, for me, it works both in iOS 10 with Swift 3 and in iOS 11 with Swift 3.
General changing the behaviour of the navigationBar should be done in viewWillAppear(_:)
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.prefersLargeTitles = true
}
After doing that it worked fine for me.
One more possible solution is to end refresh in your refreshHandler(). like this-
#objc func refreshPage() {
self.refreshControl?.endRefreshing() //End here
self.loadTableData() //Get fresh data and reload table
}
I solved this issue via storyboard
Navigation Controller -> Navigation Bar -> Attributes inspector -> Prefers Large Titles(Checked)
View Controller -> Navigation Item -> Attributes inspector -> Large Title (Automatic or Always checked)
I think It does seem a little bit dummy but I effectively solved the problem with this:
self.navigationItem.prompt = ""
self.navigationItem.prompt = nil
It's like navigationBar needs a sort of update in one of its elements to update the layout.
Sometimes to update something in navigationBar I need to hide and unhide it.. That's why I think there is a best way to do it.. For the moment that's my workaround.
I had a similar issue with navigation bar, but in my case it had a custom title view, and navigation bar remained empty until table view is scrolled down, which triggered UILayoutContainerView to layout its subviews, one of which are navigation controller's view and navigation bar. I assume the root of it is the same as the large title navigation bar issue.
Anchoring tableView to the safeAreaLayoutGuide didn't work out for me, largeTitleDisplayMode couldn't be other then .never
So I managed to fix it by calling self.navigationController?.view.setNeedsUpdateConstraints in the top presented controller's viewDidAppear(animated:) function, or scheduling this call for the next run loop in viewWillAppear(animated:), like:
DispatchQueue.main.async {
self.navigationController?.view.setNeedsUpdateConstraints()
}
In this case, navigation bar appeared with the correct content and size along with presenting transition, instead of popping in after transition was completed
What worked for me is setting the self.navigationController?.navigationBar.prefersLargeTitles = true before calling tableviews reload method.
The answers above for tableview also works for collection view if parent view not scrolled before navigation:
self.collectionView.contentInsetAdjustmentBehavior = .never
If parent view scrolled before navigation the solution above did not work in my case. I had to add the answer above as:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.sizeToFit()
}
That does seem like a weird behavior at first, but try setting the navigationItem.largeTitleDisplayMode to always. The default is automatic - and it's not defined how that works in the docs.
Also wrote / will update an answer about large titles here.
I had the similar issue. The view is a table view. The property of prefersLargeTitles is set at viewDidLoad event. Then I set view's title in viewWillAppear event.
override open func viewDidLoad() {
if #available(iOS 11.0, *) {
self.navigationController?.navigationBar.prefersLargeTitles = true
} else {
// Fallback on earlier versions
}
...
}
override open func viewWillAppear(_ animated: Bool) {
self.navigationItem.title = "something"
...
}
In my prepare segue event, I set navigation item's tile to nil so that the next view left navigation var item displays "Back" automatically.
override func prepare(for segue: UIStoryboardSegue,
sender: Any?) {
self.navigationItem.title = nil
...
}
The first time the table view displays large title correctly. However, if I select a row to next view and back to the table view, the navigation item's title becomes empty.
After several hours' struggling, I finally find out that the view's title should be set in viewDidAppear event! It seems that whatever set to view's title in Will event would be reset by UIKit internally back to nil. So it has to be set in a different event.
override func viewDidAppear(_ animated: Bool) {
self.navigationItem.title = "something"
...
}
override open func viewWillAppear(_ animated: Bool) {
// self.navigationItem.title = "something" // Remove it and set title in Did event!
...
}
Before I introduced this iOS 11 new feature, my app runs OK. It seems that the new feature has some changes in UIKit so that the previous version app may need some updates/changes to make it work.
I had the same issue and fixed it by changing order of views in my ViewController in InterfaceBuilder.
It seems like if the first view in Hierarchy is NOT a ScrollView then NavigationBar appears in LargeTitle mode and does not animates together with scroll view. If you need to have Navigation Bar title to reflect your scroll then you need to put your scroll view as the first in view hierarchy.
Also, I am not completely sure in this but looks like Navigation Bar appearance in standard or Large Title mode depends on views hierarch of previous Controller.
Similar issue for me with a UITableViewController added to a UIViewController. In my instance, these view controllers are themselves embedded in a UITabBarController and only the first tab displayed correctly used a large title. Other tabs required a manual scroll before the large title was displayed.
The only thing I could get to work was adjusting the contentInset as per #pau-senabre's answer, except I the top inset wasn't helpful for me. Instead, I set the left inset and then reset it on the next runloop.
private var isFirstAppearance = true
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if isFirstAppearance {
applyLargeTitlesFix()
}
}
private func applyLargeTitlesFix() {
let originalInset = tableViewController.tableView.contentInset
tableViewController.tableView.contentInset = UIEdgeInsets(top: 0, left: 1, bottom: 0, right: 0)
DispatchQueue.main.async { [weak self] in
self?.tableViewController.tableView.contentInset = originalInset
}
isFirstAppearance = false
}
Same issue here with Swift 4.2, iOS 12, and refactored Storyboards.
Tried adding prefersLargeTitles = true to viewWillAppear and viewDidLoad, but neither fixed my issue.
Instead, I copied the refactored storyboards back into main.storyboard and found the option to enable large titles in IB. Set that option, then refactored the storyboards back out and everything is working now. For some reason, the initial refactoring stripped out the option and I couldn't enable it programmatically.
I had the same issue (iOS 14, Xcode 12.2).
It only affected navigation controllers displaying table views.
I had originally set tableView.tableFooterView = UIView() to get rid of extra separators after the last cell. Setting the footer view to nil fixed the scrolled-up navigation title.
I had same issue.
I have set below code in viewdidload method. and it get fixed.
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationItem.largeTitleDisplayMode = .never

UINavigationBar overlaps UITableView when programmatically setting prompt

I have a UINavigationController which contains a UITableViewController. This navigation controller pushes other UITableViewControllers around and eventually these table view controllers will have a prompt.
The problem is when I set this prompt programmatically it overlaps the content of the table view underneath it.
(A search bar is being hidden by the navigation bar)
I was looking around in SO and found this answer. I tried the suggestion there in two different ways in the affected view controller but nothing changed:
override func viewDidLoad() {
super.viewDidLoad()
self.edgesForExtendedLayout = .None;
self.extendedLayoutIncludesOpaqueBars = false;
self.navigationItem.title = NSLocalizedString("Add Anime or Manga", comment: "")
self.navigationItem.prompt = NSLocalizedString("Search media belonging to this series.", comment: "")
}
-
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = NSLocalizedString("Add Anime or Manga", comment: "")
self.navigationItem.prompt = NSLocalizedString("Search media belonging to this series.", comment: "")
self.edgesForExtendedLayout = .None;
self.extendedLayoutIncludesOpaqueBars = false;
}
A comment in that same answer linked to this Apple guide on preventing views from overlapping each other. The problem is UITableViewController doesn't appear to have top/bottom layout guides so I can't create a constraint (another SO answer says having said layouts in table view controllers is irrelevant).
As such I have exhausted all my options.
I have tried to reproduce your problem and it seems that when not all the viewControllers have a prompt the navigationBar is somehow not resizing properly.
It seems you need to somehow trigger the layouting for the UINavigationController. The only way I could make it work properly was by adding this in viewWillAppear:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES];
[self.navigationController setNavigationBarHidden:NO];
}
Maybe this prompt is meant to be used consistently across the entire application (meaning having one for all viewControllers or none of them), that's why the UINavigationController does not layout it's subviews when it changes.
Hope this works for you too.
Select your TableViewController from document outline and change the value to translucent navigation bar of top bar in attributes inspector. Be sure that you will not select uitableview you should select your your table view controller(aka File's Owner) from document outline.
You have to set prompt only if view did appear, then it works:
override func viewDidAppear(_ animated: Bool) {
navigationItem.prompt = "your prompt here"
}
It's 2019 and this is still not fixed. Slow Clap. I refuse to be cowed by such things so I hammered iOS into submission with the dirtiest trick in the book. I fixed this by doing a disgusting -44 "y trick" on the UINavigation while placing the UITableView in top 44, I know it's stupid, but it works.. I am sure new fangled phones will ruin my genius.. but hey ho.. I have lazily left irrelevant code (because I am idle) but hopefully you can see what I did.
WITHOUT THE y: -44 Hack
WITH THE y: -44 Hack
let screenSize: CGRect = UIScreen.main.bounds
let navBar = UINavigationBar(frame: CGRect(x: 0, y: -44, width: screenSize.width, height: 44)) //<<--note minus 44
navBar.barTintColor = Globals.Color_BackgroundGrey()
navBar.isTranslucent = false
tableView.contentInset = UIEdgeInsets(top: 44, left: 0, bottom: 0, right: 0); //<--note plus 44
self.edgesForExtendedLayout = []
let navItem = UINavigationItem(title: "Boaty Mc Boatface")
let doneItem = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(done))
navItem.rightBarButtonItem = doneItem
navBar.setItems([navItem], animated: false)
view.addSubview(navBar)

Resources