Safe area not working in custom TabBarController's ViewController - swift

In the didFinishLaunchingWithOptions of my app's AppDelegate, after initializing my UIWindow property, I set the root controller to a subclass of UITabBarController, in which I set the view controllers (and some other custom behaviour).
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = TabBarController() // a subclass of UITabBarController
self.window?.makeKeyAndVisible()
return true
}
The issue I have is that the safe areas in the TabBarController's view controllers does not seem to work properly.
eg: sticking a view to the bottom of a view controller's view, using it's safeAreaLayoutGuide, goes behind the tab bar.
self.bottomView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor)
The custom behaviour (such as set tint colors, set the viewControllers and their tab bar items, or set the tabBarController's delegate - nothing crazy here) setup is made in it's viewDidLoad.
NB: I work in code only, I do not use interface builder.

The issue was the way I presented programmatically the tab bar controller.
What I did in the AppDelegate's didFinishLaunchingWithOptions was :
self.window?.rootViewController = MyTabBarController()
self.window?.makeKeyAndVisible()
I fixed the issue buy putting the tabbar in in a NavigationController :
let navigationController = UINavigationController(rootViewController: MyTabBarController())
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()

Can't reproduce. Here's the complete code for a view controller in a project that does what you seem to describe:
class FirstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .red
self.view.addSubview(v)
v.widthAnchor.constraint(equalToConstant: 100).isActive = true
v.heightAnchor.constraint(equalToConstant: 100).isActive = true
v.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
v.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor).isActive = true
}
}
And here's the result; the red square appears exactly where we expect to see it, pinned to the top of the tab bar:

Related

NavigationBar title overlapped with new opened VC after active and cancel search bar in 1st VC

I have a navigation bar which includes a search bar to filter the table data in current VC, let's call it 1st VC.
And the steps is active this search bar and cancel it. Then click any row of tableView 1st VC to open 2nd VC. The weird behavior is coming, the navigation title of 2nd VC is overlapped with 1st VC's nav title. Also the button on top-right is not clickable anymore after this issue happened. I get this when upgrade to iOS 13 from previous version 12.
/ / / attach the issue screenshot firstly, you could see that title "Music" in 1st VC is overlapped with title "Playing" in 2nd VC.
/ / / nav bar code in 1st VC
override func viewDidLoad() {
super.viewDidLoad()
// set tableView's separatorColor
self.tableView.separatorColor = UIColor(rgb: 0x5E5E5E)
// get songs from app's local dir
self.retrieveSongsfromLocalDir()
// add search bar
resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.hidesNavigationBarDuringPresentation = false
controller.obscuresBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
// set search Bar covered color, same with tableView's background color.
controller.searchBar.barTintColor = UIColor(rgb: 0x292f33)
return controller
})() // closure, learn it later!!
navigationItem.searchController = resultSearchController
// reload the tableView
self.tableView.reloadData()
}
/ / / I put the custom code of navigation bar in AppDelegate.swift for global use.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Custom Navigation Bar's appearance.
UINavigationBar.appearance().tintColor = UIColor.white
UINavigationBar.appearance().barTintColor = UIColor(rgb: 0x55acee)
UINavigationBar.appearance().isTranslucent = false
UINavigationBar.appearance().clipsToBounds = false
// UINavigationBar.appearance().backgroundColor
UINavigationBar.appearance().titleTextAttributes = [.foregroundColor: UIColor.white, .font: UIFont.boldSystemFont(ofSize: 23)]
return true
}
/ / / Update, it is duplicate with thread https://stackoverflow.com/questions/58134631/ios-13-uibarbuttonitem-not-clickable-and-overlapping-uinavigationbars-when-using. It seems the bug of Apple from iOS 13, and temporary fix is to set hidesNavigationBarDuringPresentation = true.

TabBar controls the NavigationBar

I added 2 ViewControllers into a TabBar, but now, the TabBar's NavigationBar 'took over' each view's NavigationBar.
I can't set a title for each one of them, I can't add buttons, nothing.
I tried a few solutions to solve it that I found on the internet, but nothing worked.
I need control over the NavigationBar of each one of the views, as I need them to be different, with different title, etc.
This is my TabBar code:
class TabBar: UITabBarController {
let homeVC = HomeVC()
let followingVC = FollowingVC()
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.titleTextAttributes = [.foregroundColor: UIColor.appColors.mainWhite]
navigationController?.navigationBar.tintColor = UIColor.appColors.mainWhite
navigationItem.setHidesBackButton(true, animated: false)
homeVC.tabBarItem = UITabBarItem(tabBarSystemItem: .topRated, tag: 0)
followingVC.tabBarItem = UITabBarItem(tabBarSystemItem: .contacts, tag: 1)
let tabBarList = [homeVC, followingVC]
viewControllers = tabBarList
}
}
I really need the option to configure each NavigationBar from it's own ViewController, or atleast from the TabBar class.
You should add UINavigationController to each of your ViewControllers not your UITabBarController
First remove the UINavigationController of your TabBar, Either you have done this pragmatically or in the storyboard remove that first.
Second add UINavigationController to you ViewControllers
class TabBar: UITabBarController {
let homeVC = HomeVC()
let followingVC = FollowingVC()
override func viewDidLoad() {
super.viewDidLoad()
homeVC.tabBarItem = UITabBarItem(tabBarSystemItem: .topRated, tag: 0)
followingVC.tabBarItem = UITabBarItem(tabBarSystemItem: .contacts, tag: 1)
let homeNavigationController = UINavigationController(rootViewController: homeVC)
let followingNavigationController = UINavigationController(rootViewController: followingVC)
let tabBarList = [homeNavigationController, followingNavigationController]
viewControllers = tabBarList
}
}
Now if you change any properties like title and barButtons it will reflect accordingly.
Figured it out, the solution is:
The NavigationBar was not the TabBar navigation bar, but the screen that lead to the TabBar (Login Screen for example), I fixed it by hidding the navigation bar of the login screen when transfering to the TabBar controller, now the navigation bar of each view controller is shown and not blocked by the Login Viewcontrolelr navigationbar.

UISearchController shifted up when typing under UINavigationController

I embedded a viewcontroller(BlueViewController) in a navigation controller.
the blueViewController has a UISearchController that set as the blueViewController's tableView headerView.
the searchController works fine except when it's active, it shift up and hide behind the UINavigationController(or somewhere)
I've tried to add the search controller's searchBar as a subview to the viewController, or positions a UIView and assigned the searchBar to that UIView, none of these works
I have tried this link, doesn't work
Strange UISearchDisplayController view offset behavior in iOS 7 when embedded in navigation bar
here is the searchController in BlueViewController
let searchController:UISearchController = {
let controller = UISearchController(searchResultsController: nil)
controller.hidesNavigationBarDuringPresentation = false
controller.obscuresBackgroundDuringPresentation = false
controller.searchBar.inputAssistantItem.leadingBarButtonGroups = []
controller.searchBar.inputAssistantItem.trailingBarButtonGroups = []
return controller
}()
In viewDidLoad
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
definesPresentationContext = true
you can also find entire project here
https://github.com/QiquanLu/TestNavigationWithSearchController
Any hint would be appreciated, thanks!
For iOS 11 and above, you should be setting the searchController property on BlueViewController's navigationItem like so:
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.searchController = searchController
}
Don't add it as the tableView's header.

Swift - Expand ViewController behind the TabBar

I created three tabItems in UITabBarViewController which is pushed to another UINavigationController. One of the three tabItems is a UINavigationController, the other two are UIViewController. In order to make sure there is only one navigation bar is shown when it is in non-root viewController of the tabItem which is a UINavigationController, the parent UINavigationBar will be hidden.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let navigationC = self.navigationController, navigationC.viewControllers.count == 1 {
self.navigationController?.setNavigationBarHidden(true, animated: false)
self.parent?.navigationController?.setNavigationBarHidden(false, animated: false)
} else {
self.navigationController?.setNavigationBarHidden(false, animated: false)
self.parent?.navigationController?.setNavigationBarHidden(true, animated: false)
}
self.setupViewConstraints()
}
The issue here is, if I navigate to the 2nd UIViewController of the UINavigationController, then switch from UINavigationController to UIViewController and back to the UINavigationController, the bottom of the UINavigationController is moved up which should equal to the bottom of the UIScreen. If I navigate back to the root view of the UINavigationController, the bottom of the UINavigationcontroller is equal to the UIScreen which is correct.
How can I make sure all UIViewControllers' bottom in the navigationController is equal to UIScreen? It may due to there are two navigationBar in the non-root viewController of UINavigationController tabItem.
Below is the code I used to create UITabBarViewController:
override func viewDidLoad() {
super.viewDidLoad()
tabBar.tintColor = UIColor.fromHexString("0078d7")
tabBar.barTintColor = UIColor.white
let firstViewController = FirstViewController()
firstViewController.delegate = self
firstViewController.tabBarItem = UITabBarItem.init(title: "first", image: , tag: 0)
let secondViewController = SecondViewController()
secondViewController.delegate = self
secondViewController.tabBarItem = UITabBarItem.init(title: "second", image: , tag: 1)
let thirdViewController = ThirdViewController()
thirdViewController.delegate = self
thirdViewController.tabBarItem = UITabBarItem.init(title: "third", image: , tag: 3)
let initialNavigationController = UINavigationController(rootViewController: firstViewController)
initialNavigationController.navigationItem.title = ""
self.addChildViewController(initialNavigationController)
self.addChildViewController(secondViewController)
self.addChildViewController(thirdViewController)
self.navigateToLastVisitedViewController(navigationController: initialNavigationController)
}
In above viewDidLoad method of UITabBarViewController just make your tabBar translucent. This will allow you content viewController to lay beneath UITabBar object.
Swift
self.tabBar.isTranslucent = true
Objective-C
[self.tabBar setTranslucent:YES];
Same logic will be use in UINavigationBar case.

Swift - TabViewController with SwipeViewControler in one of its tabs

Another newbie to swift here. I want my application to have four tabs on the bottom and inside one of the tabs I want to be able to swipe between two 'pager' style tabs.
I have been able to make the four tabs work at the bottom by starting a new tabbed app and adding two additional view controllers.
Then I tried adding a SwipeViewController (https://github.com/fortmarek/SwipeViewController) to one of the tabs and it worked but it overrode the entry point so all I see is the two SwipeView tabs without the TabController tabs on the bottom.
!I want this inside of the Home tab]1
I think the problem is in my delegate code as it is setting self.window?.rootViewController to the SwipViewController. Getting rid of that assignment makes the Tab Bar show back up, but it is black inside of the Home tab so it is still not working.
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let pageController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
let navigationController = SecondViewController(rootViewController: pageController)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
return true
}
I also tried getting the corresponding window for the "Home" tab from the storyboard and setting the navigation controller to that.
let secondViewControllerWindow = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Home").view.window
secondViewControllerWindow?.rootViewController = navigationController
secondViewControllerWindow?.makeKeyAndVisible()
But Home is still empty. Here is the ViewController code that is represented by the Home tab and should control the SwipViewController
import UIKit
class SecondViewController: SwipeViewController {
override func viewDidLoad() {
super.viewDidLoad()
let stb = UIStoryboard(name: "Main", bundle: nil)
let page_one = stb.instantiateViewController(withIdentifier: "nba") as UIViewController
let page_two = stb.instantiateViewController(withIdentifier: "mlb") as UIViewController
setViewControllerArray([page_one, page_two])
page_one.title = "NBA"
page_two.title = "MLB"
setFirstViewController(0)
equalSpaces = true
//setButtons(UIFont.systemFontOfSize(18), color: UIColor.blackColor())
//setSelectionBar(80, height: 3, color: UIColor.blackColor())
setButtonsOffset(40, bottomOffset: 5)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
My questions is similar to these other questions but I did not find an answer.
Using Page View Controller inside Tab Swift
Xcode 6 - Swift - Custom Tabbar with Navigation
I faced the same problem recently, and I found the solution here.
https://github.com/fortmarek/SwipeViewController/issues/26
I didn't put the code in didFinishLaunchingWithOptions.
I tried to work on main.storyboard.
I dragged a PageViewController in and set it as your(SecondViewController)'s RootView.
It worked for me.