Tabbar controller unexpected dismiss behavior - ios

I have a UITabBarController with two childs (A and B) and I present a third controller C from UITabBarController but if I call dismiss on controller A, controller C gets dismissed.
How can this happen even if controller C is not part of controller A's hierarchy? If this is the default behavior can I change it?
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
let tab = UITabBarController()
let a = A()
let b = B()
tab.viewControllers = [a, b]
window?.rootViewController = tab
window?.makeKeyAndVisible()
DispatchQueue.main.asyncAfter(deadline: .now()+5) {
tab.present(C(), animated: true, completion: nil)
}
return true
}
}
class A: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
DispatchQueue.main.asyncAfter(deadline: .now()+10) {
self.dismiss(animated: true, completion: nil)
}
}
}

Related

Why navigation controller is nil even though I push it

I have a LoginViewController and want to push MainTabBarController
So, I do this
let controller = MainTabBarController()
navigationController?.pushViewController(controller, animated: true)
LoginViewController has a navigation controller but MainTabBarController doesn't for some reasons.
I have two questions, if my flow is LoginViewController -> MainTabBarController -> Other Controllers embedded in MainTabBarController, and I want to log out, how do I get to the LoginViewController without memory leaks? I was thinking about something like popToRootViewController but not sure if it would work.
This is how I'm creating other controllers in MainTabBarController
private func createNewViewController(viewController: UIViewController, title: String, imageName: String) -> UIViewController {
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.tabBarItem = UITabBarItem(title: title, image: UIImage(systemName: imageName), selectedImage: nil)
navigationController?.view.backgroundColor = .white
viewController.navigationItem.title = title
return viewController
}
But it doesn't really work since navigationController is nil. I don't use storyboards at all though.
EDIT: Apparently embedding a UITabBarController inside a UINavigationController is not supported.
Try this design. When LoginVC appears it checks if user is logged in and re-directs to MainVC. This is just a skelton, add more details as you go forward:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
window = UIWindow()
window?.makeKeyAndVisible()
window?.rootViewController = LoginVC()
return true
}
}
class LoginVC: UIViewController {
var isUserLoggedIn = true
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
view.backgroundColor = .red
if isUserLoggedIn {
present(MainVC(), animated: true)
}
}
}
class MainVC: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
modalPresentationStyle = .fullScreen
let firstVC = UIViewController()
firstVC.tabBarItem = UITabBarItem(title: "firstVC", image: nil, selectedImage: nil)
let secondVC = UIViewController()
secondVC.tabBarItem = UITabBarItem(title: "secondVC", image: nil, selectedImage: nil)
viewControllers = [firstVC, secondVC]
}
}

Items of UITabBarController not showing

I am trying to implement bottom navigation bar for my iOS application. However, when I am creating tabBarItem, it is not showing on TabBar. TabBar is displaying correctly. I cannot figure out where is the problem, any help will be very appreciated.
If any additional information is required, please give me a sign. My code (simplified):
AppDelegate:
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = TabBarController()
return true
}
}
TabBarController:
class TabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let homeController = HomeController()
let navigationController = UINavigationController(rootViewController: homeController)
navigationController.title = "Home"
navigationController.tabBarItem.image = UIImage(named: "icon")
viewControllers = [homeController]
}
}
HomeController:
class HomeController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarController?.tabBar.isHidden = false
}
}
EDIT:
I removed not crucial parts of the code, like isLoggedIn() function call, mentioned in the comments and changed MainNavigationController to TabBarController.
According to Matts answer I also changed this line in a TabBarController (but still bar item is not showing for some reason):
viewControllers = [navigationController]
The problem is this line:
viewControllers = [homeController]
homeController is not navigationController. So what happened to navigationController? Nothing. It vanished in a puff of smoke. You created navigationController but then you threw it away.
So nothing you say about navigationController and its configuration (including its tab bar item) has any effect; it is not in the interface (or anywhere else).
This is my complete test code (based on your code):
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = MainNavigationController()
return true
}
}
class MainNavigationController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let homeController = HomeController()
let navigationController = UINavigationController(rootViewController: homeController)
navigationController.tabBarItem.title = "MyCoolTitle"
viewControllers = [navigationController] // not [homeController]
}
}
class HomeController: UIViewController {
}

Navigation Between UIViewControllers Swift 2.2

I have 3 ViewControllers and want to navigate between then without appearing the second one.
what actually i want to do something like this:
FirstController
override func viewDidLoad() {
super.viewDidLoad()
let secondController = SecondController();
secondController.showThird();
}
SecondController
override func viewDidLoad() {
super.viewDidLoad()
super.didReceiveMemoryWarning()
}
func showThird() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewControllerWithIdentifier("thirdId")
self.navigationController?.pushViewController(controller, animated: true)
}
ThirdController
override func viewDidLoad() {
super.viewDidLoad()
}
appdelegate.swift
You can push the second Viewcontroller without animation and next line you need to push Third Viewcontroller
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let controllerOne = storyboard.instantiateViewControllerWithIdentifier("oneId")
self.navigationController?.pushViewController(controllerOne, animated: false)
let controllerTwo = storyboard.instantiateViewControllerWithIdentifier("secondId")
self.navigationController?.pushViewController(controllerTwo, animated: false)
let controllerThree = storyboard.instantiateViewControllerWithIdentifier("thirdId")
self.navigationController?.pushViewController(controllerThree, animated: true)
}
Hopefully It works for you.

Modal presentation of UITableViewController from UITabBarController

So as per title, I'm trying to present a UITableViewController from a UITabBarController. If you look at the video I've attached, on the third tab, it presents a view controller modally. Which is what I want to achieve.
https://vid.me/B0oy
I've been searching all day for a solution, and I yet I've tried them, but it doesn't work.
This is currently how I do it. I add this line of code in the view controller of my first tab.
self.tabBarController?.delegate = UIApplication.sharedApplication().delegate as? UITabBarControllerDelegate
And this is inside my AppDelegate.
class AppDelegate: UIResponder, UIApplicationDelegate, UITabBarControllerDelegate {
var window: UIWindow?
func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {
if viewController is YourViewController {
if let newVC = tabBarController.storyboard?.instantiateViewControllerWithIdentifier("YourVCStoryboardIdentifier") {
tabBarController.presentViewController(newVC, animated: true, completion: nil)
return false
}
}
return true
}
func setStatusBarBackgroundColor(color: UIColor) {
guard let statusBar = UIApplication.sharedApplication().valueForKey("statusBarWindow")?.valueForKey("statusBar") as? UIView else {
return
}
statusBar.backgroundColor = color
}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
setStatusBarBackgroundColor(UIColor.lightGrayColor())
return true
}

Swift View Layout below tabbar

class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.backgroundColor = UIColor.whiteColor()
self.window?.makeKeyAndVisible()
let vc = ViewController()
vc.tabBarItem = UITabBarItem(...)
...
let tabbar = UITabBarController()
tabbar.setViewControllers([...,vc,...], animated: false)
self.window?.rootViewController = tabbar
tabbar.selectedIndex = 2
return true
}
}
class ViewController: UIViewController {
override func loadView() {
super.loadView()
self.view.backgroundColor = UIColor.yellowColor()
//self.automaticallyAdjustsScrollViewInsets = false;
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
}
}
I am not using a story board.
The above causes the ViewControllers view to extend below the tab bar. How can i stop this?
I've tried setting the views frame to
CGRectMake(0, 0, self.view.frame.width, self.view.frame.height - tabBarController.view.frame.height))
but that did not work.
You can use the edgesForExtendedLayout property of UIViewController to set which edges to extend under navigation bars. If you don't want any, you can simply say:
self.edgesForExtendedLayout = .None
For Swift 5 or above
self.edgesForExtendedLayout = []

Resources