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 = []
Related
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 {
}
When I try to add a custom UINavigationBar to a view controller like this
class ViewController: UIViewController
{
static let nav_bar_height: CGFloat = 64
let nav_bar: UINavigationBar =
{
let nav_bar = UINavigationBar()
nav_bar.translatesAutoresizingMaskIntoConstraints = false
nav_bar.backgroundColor = .blue
return nav_bar
}()
override func viewDidLoad()
{
super.viewDidLoad()
view.backgroundColor = .yellow
view.addSubview(nav_bar)
nav_bar.heightAnchor.constraint(equalToConstant: ViewController.nav_bar_height).isActive = true
nav_bar.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
nav_bar.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
nav_bar.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width).isActive = true
}
}
two distinct bars show up.
Any idea on why there is the white colored bar with a smaller height?
This is the AppDelegate
#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()
let vc = ViewController()
window?.rootViewController = vc
// Override point for customization after application launch.
return true
}...
You're probably in navigation interface with a UINavigationController as your view controller's parent.
So the second navigation bar is the UINavigationController's navigation bar.
I have a UITabBarController() that I use and assign in AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
showTabBar()
return true
}
func showTabBar() {
let tabBarVC = TabBarVC()
if let window = self.window {
window.rootViewController = tabBarVC
}
}
I have the following key is in info.plist:
In my Target under General, I have the following setting:
I use the following code in one of my tabs to hide the Status Bar:
class ViewController: UIViewController {
var statusBarShouldBeHidden = false
override func viewDidLoad() {
super.viewDidLoad()
}
override var prefersStatusBarHidden: Bool {
return statusBarShouldBeHidden
}
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .slide
}
#IBAction func buttonHideShowStatusBarTapped(_ sender: UIButton) {
statusBarShouldBeHidden = !statusBarShouldBeHidden
UIView.animate(withDuration: 0.25) {
self.setNeedsStatusBarAppearanceUpdate()
print("animating")
}
}
}
When the button is tapped, "animating" prints in the log; however, the status bar does not hide.
I am not sure if this is related to UITabBarController(), but the code above seems to work fine in a project without it.
How can I hide the status bar in iOS10 when using UITabBarController()?
You have taken TabBarVC as UIViewController subclass rather than UITabBarController subclass and then initialised and added the UITabBarController instance to it's view, I think TabBarVC should be subclass of UITabBarController and should be the rootViewController of the window. If you change the TabBarVC to subclass of UITabbarViewController status bar is working fine. Check the code below
class TabBarVC: UITabBarController, UITabBarControllerDelegate, UINavigationControllerDelegate {
//var mainTabBarController = UITabBarController() //not needed
init() {
super.init(nibName: nil, bundle: nil)
self.delegate = self
self.navigationController?.delegate = self
self.selectedIndex = 0
self.customizableViewControllers = []
self.setViewControllers(self.topLevelControllers(), animated: false)
}
You need to setNeedsStatusBarAppearanceUpdate() in your root view controller, i.e. TabBarVC. Here is the solution:
Override prefersStatusBarHidden in TabBarVC to return value of selectedViewController
override var prefersStatusBarHidden: Bool {
return mainTabBarController.selectedViewController?.prefersStatusBarHidden ?? false
}
Add reference to TabBarVC in ViewController class
var tabBarVC: UIViewController?
Set tabBarVC variable on topLevelControllers() method
let one = self.viewControllerFromStoryBoard(storyboardName: "One",
sceneName: "Initial",
iconName: "",
title: "Tab One") as! ViewController
one.tabBarVC = self
Finally, on your #IBAction update your status bar
self.tabBarVC?.setNeedsStatusBarAppearanceUpdate()
So here's the situation. I have an app which presents a tabBarController as its root view controller, with a number of tabItems. So here's the setup in my app delegate
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let vc1 = VC1(nibName: "VC1", bundle: nil)
let vc2 = VC2(nibName: "VC2", bundle: nil)
let menuTabBarController = MenuTabBarController(nibName: "MenuTabBarController", bundle: nil)
menuTabBarController.viewControllers = [VC1,VC2]
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window?.rootViewController = menuTabBarController
window?.makeKeyAndVisible()
//Setup tab bar
UITabBar.appearance().barTintColor = UIColor.blackColor()
let IMG1 = UIImage(named: "img2.png")
let IMG2 = UIImage(named: "img2.png")
VC1.tabBarItem = UITabBarItem(
title: "View 1",
image: IMG1,
tag: 1)
VC2.tabBarItem = UITabBarItem(
title: "View 2",
image: IMG2,
tag:2)
return true
}
func applicationWillResignActive(application: UIApplication) {
}
func applicationDidEnterBackground(application: UIApplication) {
}
}
So notice I have two UIViewController classes(VC1 and VC2) nested within the menuTabBar. Now from menuTabBar class, I can easily switch between the views like so
class MenuTabBarController: UITabBarController,UITabBarControllerDelegate{
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self//Set tabbarcontroller delegate
}
// UITabBarControllerDelegate
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {
tabBarController.selectedIndex = 1;//Say I wanted to transition to VC2
}
}
But What I ultimately want to do is to be able to switch between the two UIViewControllers from another class that is not a UIViewController. I attempted something like this
class Actions(){
func someFunctionThatSwitchesToVC2(){
//So I create a reference to appDelegate which in turn, is supposed to reference the tab bar and switch between its child views
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let tabBarController: UITabBarController = (appDelegate.window?.rootViewController as? UITabBarController)!
tabBarController.selectedIndex = 1
}
}
When I run the someFucntionThatSwitchesToVC2, I get this error "fatal error: Index out of range". Obviously I'm not doing something right. Any ideas greatly appreciated
PS, When I do tabBarController.selectedIndex = 0, I don't get the error but nothing happen
I think you are doing typo while assigning ViewControllers. Change this line:
menuTabBarController.viewControllers = [VC1,VC2]
with below line:
menuTabBarController.viewControllers = [vc1,vc2]
I'm rebuilding an app without storyboards and the part of it that I'm having the most trouble with is navigating view-to-view programatically. Few things are written out there which don't use storyboards, so finding an answer for this has been tough.
My problem is pretty simple. I have my ViewController and my SecondViewController and I want to push from the former to the latter.
In AppDelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window?.backgroundColor = UIColor.whiteColor()
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
return true
}
Then in ViewController.swift:
class ViewController: UIViewController, AVAudioPlayerDelegate, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
startFinishButton.setTitle("Begin", forState: .Normal)
startFinishButton.addTarget(self, action: "moveToSecondViewController", forControlEvents: .TouchUpInside)
view.addSubview <*> startFinishButton
}
func moveToSecondViewController(sender: UIButton) {
let vc = SecondViewController()
println(self.navigationController) // returns nil
self.navigationController?.pushViewController(vc, animated: true)
}
}
Printing self.navigationController returns nil. I've tried doing:
var navController = UINavigationController() when the ViewController class is created (but outside of ViewDidLoad, right under the class declaration) and done the push using the navController var but that hasn't worked.
I'm thinking maybe the solution is to create a navigation controller in App Delegate that the whole app would use, I guess as a global variable?
My hope is that this post can serve many others who are new to Swift and want to remove storyboards from their app.
Thanks for taking a look and for your help.
In Swift 3
Place this code inside didFinishLaunchingWithOptions method in AppDelegate class.
window = UIWindow(frame: UIScreen.main.bounds)
let mainController = MainViewController() as UIViewController
let navigationController = UINavigationController(rootViewController: mainController)
navigationController.navigationBar.isTranslucent = false
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
In AppDelegate
var window: UIWindow?
var navController: UINavigationController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
navController = UINavigationController()
var viewController: ViewController = ViewController()
self.navController!.pushViewController(viewController, animated: false)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.rootViewController = navController
self.window!.backgroundColor = UIColor.whiteColor()
self.window!.makeKeyAndVisible()
return true
}
In ViewController
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "FirstVC"
var startFinishButton = UIButton.buttonWithType(UIButtonType.System) as! UIButton
startFinishButton.frame = CGRectMake(100, 100, 100, 50)
startFinishButton.backgroundColor = UIColor.greenColor()
startFinishButton.setTitle("Test Button", forState: UIControlState.Normal)
startFinishButton.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(startFinishButton)
}
func buttonAction(sender:UIButton!)
{
println("Button tapped")
let vc = SecondViewController()
self.navigationController?.pushViewController(vc, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
In Swift 5 and Xcode 13 there is a SceneDelegate along with the AppDelegate. So now to completely remove the storyboard from a project and embed the view controller in a navigation controller do the following:
Delete the actual storyboard in the Project Navigator
Select the project in the Project Navigator, select Target and then the General tab, then delete the storyboard from Main Interface
In the info.plist file delete the storyboard from:
Application Scene Manifest > Scene Configuration > Application Session Role > Item 0 (Default Configuration) > Storyboard Name
Then in the scene delegate change the scene function to look like this:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
let navigationController = UINavigationController(rootViewController: YourViewController())
window.rootViewController = navigationController
self.window = window
window.makeKeyAndVisible()
}