So when I have my OptionsViewController as the rootViewController in the AppDelegate didFinishLaunchingWithOptions...
let rootVC = OptionsViewController()
let navigationController = UINavigationController(rootViewController: rootVC)
navigationController.navigationBar.barTintColor = .white
navigationController.navigationBar.isTranslucent = false
navigationController.navigationBar.tintColor = .black
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window!.rootViewController = navigationController
self.window?.makeKeyAndVisible()
...setting the title of the OptionViewController works if I do this in viewDidLoad():
title = "Route Options"
But when I push OptionsViewController onto the navigation stack the title doesn't show up.
I.e. if I start w/ a different view as the rootViewController in AppDelegate:
let rootVC = HomeViewController()
let navigationController = UINavigationController(rootViewController: rootVC)
navigationController.navigationBar.barTintColor = .white
navigationController.navigationBar.isTranslucent = false
navigationController.navigationBar.tintColor = .black
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window!.rootViewController = navigationController
self.window?.makeKeyAndVisible()
And in HomeViewController I push my OptionViewController like this:
let optionsVC = OptionsViewController()
navigationController?.pushViewController(optionsVC, animated: true)
The title does not show up!
The only way I've managed for the title to show up is by doing (in OptionViewController)
navigationController?.navigationBar.topItem?.title = "Route Options"
But it shows up as the back button rather than in the middle, which is not what I want.
If anyone could tell me how I could set the title so that it is on the middle of the navigation bar when it is pushed onto the navigationController stack that would be great!
Code
AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let rootVC = HomeViewController()
let navigationController = UINavigationController(rootViewController: rootVC)
let barAppearance = UINavigationBar.appearance()
barAppearance.barTintColor = UIColor.blue
barAppearance.tintColor = UIColor.white
barAppearance.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window!.rootViewController = navigationController
self.window?.makeKeyAndVisible()
return true
}
HomeViewController.swift
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, DestinationDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let optionsVC = OptionsViewController()
self.definesPresentationContext = false //else going to try and present optionVC on homeVC when in optionVC
navigationController?.pushViewController(optionsVC, animated: true)
}
tableView.deselectRow(at: indexPath, animated: true)
}
}
OptionsViewController.swift
class OptionsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource,
DestinationDelegate, SearchBarCancelDelegate,UISearchBarDelegate,
CLLocationManagerDelegate {
override func viewDidLoad() {
self.title = "Route Options"
}
You need to set the navigationItem.title to desired value. If you want an image you set navigationItem.titleView
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "Your title here"
}
For others coming here based on the title, don't forget to set your ViewController class in IB to the appropriate Swift file.
After doing that I was able to set the title without a problem using
self.navigationItem.title = "my title"
or
self.title = "my title"
Try it:
In HomeViewController:
let optionsVC = OptionsViewController()
navigationController?.viewControllers = [optionsVC]
And in your OptionsViewController:
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.isTranslucent = false
navigationItem.title = "Your Title"
}
first you need to set UINavigationBar color and text color .
try this in didFinishLaunchingWithOptions.
let barAppearance = UINavigationBar.appearance()
barAppearance.barTintColor = UIColor.blue
barAppearance.tintColor = UIColor.white
barAppearance.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
if you want to remove the string after backbutton
add these too
let barItemAppearace = UIBarButtonItem.appearance()
barItemAppearace.setBackButtonTitlePositionAdjustment(UIOffsetMake(0, -60), for:UIBarMetrics.default)
And just set your title in viewDidLoad() or
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.title = "Your Title"
}
Just add the below line to set the title for navigation item
self.title = "Title 1"
Using Objective-C I also had the problem that it didn't show me the title but going to the next Scene the title appeared next to the back button.
I don't really know why, but I solved it by programming the relative ViewController of that Scene in Swift instead of Objective-C.
After that, it was enough to use the command:
self.title = "My Title"
and so I was able to write what I want programmatically, using if-statement or other methodologies.
Maybe this could be useful to someone who has this problem with Objective-C.
All you need is initialisation navigationItem.title by string containing any printable symbols in viewDidLoad function. String like "" or " " will not worked.
Like this:
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "."
}
Then code navigationItem.title = "Any string" will work well anywhere in your ViewController.
This bag was fixed in iOs 16.2
Related
I have an UITabBarController as my rootViewcontroller which has 3 UINavigationController for each Tab. Each UINavigationController has an initial UIViewController which just has a red background color.
My Problem is that the UINavigationController does not completely cover the UIViewController at the first start. After switching the tabs it covers the UIViewController. So what am I doing wrong here?
Thanks in advance.
AppDelegate:
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = TabBarViewController()
window?.makeKeyAndVisible()
UITabBarController:
class TabBarViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
// Navigation Tab
let navVC = NavigationViewController()
// Departure Tab
let depVC = DeparturesViewController()
// Settings Tab
let setVC = SettingsViewController()
self.viewControllers = [
createNavigationController(title: "Navigation", rootViewController: navVC, imageName: "map"),
createNavigationController(title: "Abfahrten", rootViewController: depVC, imageName: "station"),
createNavigationController(title: "Einstellungen", rootViewController: setVC, imageName: "user"),
]
}
private func createNavigationController(title: String, rootViewController: UIViewController, imageName: String) -> UINavigationController {
rootViewController.title = title
let nc = UINavigationController(rootViewController: rootViewController)
nc.title = title
nc.view.backgroundColor = .white
nc.navigationBar.prefersLargeTitles = true
nc.navigationController?.navigationItem.largeTitleDisplayMode = .always
nc.tabBarItem.image = UIImage(named: imageName)?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
return nc
}
override func viewWillAppear(_ animated: Bool) {
self.selectedIndex = 0
}
}
The very simple UIViewController:
class NavigationViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .red
}
}
After removing following code form the TabBarController it completely wraps the Viewcontroller
override func viewWillAppear(_ animated: Bool) {
self.selectedIndex = 0
}
But I can't explain why
I need to add UITabBarController as a subview of RootViewController, but that UITabBarController can't be touched.
Here is my code. How can i fix it?
func addSubviewToSelf(){
var tabVC = TabBarVC()
addChildViewController(tabVC)
self.view.addSubview(tabVC.view)
tabVC.didMove(toParentViewController: self)
tabVC.view.snp.makeConstraints{ (make) in
make.top.bottom.left.right.equalTo(self.view)
}
}
Here is the simplified version of adding UITabBarController as childView of UIViewController, I'm using NSLayoutAnchor API in place of SnapKit
import UIKit
class RootViewController: UIViewController {
override func loadView() {
view = UIView()
view.backgroundColor = .white
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
addChildVC()
}
func addChildVC() {
let tabBarVC = TabBarVC()
addChild(tabBarVC)
view.addSubview(tabBarVC.view)
tabBarVC.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tabBarVC.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tabBarVC.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tabBarVC.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
tabBarVC.view.topAnchor.constraint(equalTo: view.topAnchor)
])
tabBarVC.didMove(toParent: self)
}
}
The UITabBarController subclass is below
class TabBarVC: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
// Do any additional setup after loading the view.
configureTabBarItems()
}
func configureTabBarItems() {
let vc1 = UIViewController()
vc1.view.backgroundColor = .orange
vc1.tabBarItem = UITabBarItem(tabBarSystemItem: .search, tag: 0)
let vc2 = UIViewController()
vc2.view.backgroundColor = .yellow
vc2.tabBarItem = UITabBarItem(tabBarSystemItem: .bookmarks, tag: 1)
let navigationController1 = UINavigationController(rootViewController: vc1)
let navigationController2 = UINavigationController(rootViewController: vc2)
setViewControllers([navigationController1, navigationController2], animated: false)
tabBar.tintColor = .red
tabBar.unselectedItemTintColor = .black
}
}
As you have not given UITabBarController subclass implementation, you may create some UIViewControllers and assign it to setViewControllers(_ viewControllers::[UIViewControllers]?, animated: Bool) method. Also set tintColor and unselectedItemTintColor properties and check if works for you. Finally Clean Build project and run, it should work for you.
If the Tabbarvc is about for all App , I think you can call in AppDelegate. Write this code in didFinishLaunch in AppDelegate
window = UIWindow(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard(name: "TabBarStoryBoard", bundle: Bundle.main)
let viewController = storyboard.instantiateInitialViewController()
window?.rootViewController = viewController
window?.makeKeyAndVisible()
return true
My app structure:
firstView: UIViewController with UINavigationController -- secondView UITabBarController with several UIViewControllers
start app, firstView:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let vc = ViewController()
let navContr = UINavigationController(rootViewController: vc)
self.window? = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = navContr
self.window?.makeKeyAndVisible()
return true
}
In firtView I click button to open secondView:
let vc = MyTabController() // my UITabBarController
self.navigationController?.pushViewController(vc, animated: true)
start secondView:
class MyTabController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let gen = MyViewController()
let tabGen = UITabBarItem()
gen.tabBarItem = tabGen
tabGen.image = UIImage(named: "general")
tabGen.title = "Все вопросы"
viewControllers = [gen]
....
}
In my secondView I want set Title, set UISearchControlleer in every tab. But if I write in ViewController
navigationItem.title = "myTitle" nothing changes. I see button "Back" only.
Here's the screenshot
Set title like this in viewDidLoad() Method
self.title = "Your Title"
I'm working on an app that uses UITabBarController and sudenlly one tabItem stops to appear. Then I start to investigate and end up with a problem related with UISearchController and UITabBarController.
To isolate the problem a build a simple app to demostrate the situation.
This is how I instanciate the TabBar at didFinishLaunchingWithOptions :
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.backgroundColor = .white
let first:SearchController = {
let sc = SearchController()
sc.tabBarItem = UITabBarItem(title: "First", image: UIImage(named:"iphone"), tag: 0)
return sc
}()
let second:SecondViewController = {
let s = SecondViewController()
s.tabBarItem = UITabBarItem(title: "Second", image: UIImage(named:"iphone"), tag: 1)
return s
}()
let tabBar = UITabBarController()
let controllers = [first, second]
tabBar.viewControllers = controllers.map {
UINavigationController(rootViewController: $0)
}
self.window?.rootViewController = tabBar
self.window?.makeKeyAndVisible()
return true
}
This is the viewController with UISearchController:
class SearchController: UIViewController {
var matchingItems:[String] = [] {
didSet{
self.tableView.reloadData()
}
}
lazy var searchController:UISearchController = UISearchController(searchResultsController: nil)
lazy var tableView: UITableView = { [unowned self] in
let tv = UITableView(frame: CGRect.zero, style: .grouped)
tv.translatesAutoresizingMaskIntoConstraints = false
tv.delegate = self
tv.dataSource = self
tv.register(UITableViewCell.self, forCellReuseIdentifier: "id")
return tv
}()
override func viewDidLoad() {
super.viewDidLoad()
title = "First"
view.addSubview(self.tableView)
self.navigationItem.searchController = searchController
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.perform(#selector(showKeyboard), with: nil, afterDelay: 0.1)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
searchController.searchBar.resignFirstResponder()
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let layout = self.view.safeAreaLayoutGuide
tableView.topAnchor.constraint(equalTo: layout.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: layout.bottomAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: layout.rightAnchor).isActive = true
tableView.leftAnchor.constraint(equalTo: layout.leftAnchor).isActive = true
}
#objc func showKeyboard() {
self.searchController.searchBar.becomeFirstResponder()
self.searchController.searchBar.text = ""
}
}
In run time, when system finished rendering this view controller, the debug console print this:
SearchBar+TabBar[15454:895018] [MC] Reading from private effective user settings.
2018-04-22 12:07:19.595887-0300 SearchBar+TabBar[15454:895018] +[CATransaction synchronize] called within transaction
2018-04-22 12:07:19.608090-0300 SearchBar+TabBar[15454:895018] +[CATransaction synchronize] called within transaction
2018-04-22 12:07:19.608269-0300 SearchBar+TabBar[15454:895018] +[CATransaction synchronize] called within transaction
2018-04-22 12:07:19.608516-0300 SearchBar+TabBar[15454:895018] +[CATransaction synchronize] called within transaction
Searching on the stackoverflow I found that this message +[CATransaction synchronize] called within transaction
is related to rendering more than one animation in the main thread.
I'm wondering if this viewcontroller visualisation problem is related to that. So I commented the searchController instantiation line in SearchController class:
self.navigationItem.searchController = searchController
Now the UITabBarController works perfectly without the UISearchController in my first view controller.
My questions are:
Is there another way to instanciate UISearchController to avoid
this?
If yes, how should I do that?
GitHub repository with the sample code: sample code
In Your TabBarController class implement this function:
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool
In this function you should check if Controller with searchBar was selected before and make SearchController inactive
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if (tabBarController.selectedIndex == ControllerWithSearchBarIndex) {
((viewControllers![ControllerWithSearchBarIndex] as! UINavigationController).viewControllers.first! as! ControllerWithSearchBarIndex).searchController.isActive = false
}
return true
}
A friend of my suggests to add UITabBarController as root of UINavigationController in the at didFinishLaunchingWithOptions and it works perfectly and solve the problem:
let tabBar = UITabBarController()
let controllers = [first, second]
tabBar.viewControllers = controllers.map {
UINavigationController(rootViewController: $0)
}
let nav = UINavigationController(rootViewController: tabBar)
nav.isNavigationBarHidden = true
nav.navigationBar.isUserInteractionEnabled = false
Set definesPresentationContext = true on the view controller with the tableView (SearchController in your example). This will allow the UISearchController to remain active when you switch tabs.
In my app I have a tabBarViewController, and this is the first bar:
class FirstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
}
override func viewWillAppear(_ animated: Bool) {
self.navigationItem.title = "RECENTS"
}
}
this is the custom class:
class CustomTabBar: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let firstVC = FirstViewController()
let secondVC = SecondViewController()
let thirdVC = ThirdViewController()
firstVC.tabBarItem.title = "RECENT"
secondVC.tabBarItem.title = "MAP"
thirdVC.tabBarItem.title = "SETTINGS"
firstVC.title = "RECENT"
secondVC.title = "MAP"
thirdVC.title = "SETTINGS"
viewControllers = [firstVC,secondVC,thirdVC]
}
}
So my question is: How can I put a title at the top of the view controller?
In the previous code I tried, but it's not working!
Something like this:
The title property is shown when a view controller is wrapped in a UINavigationController. Here's some code replacing the last line (i.e. the viewControllers assignment) in of your example that should do the trick:
let firstNC = UINavigationController(rootViewController: firstVC)
let secondNC = UINavigationController(rootViewController: secondVC)
let thirdNC = UINavigationController(rootViewController: thirdVC)
viewControllers = [firstNC,secondNC,thirdNC]
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.navigationItem.title = "My Title"
}
This should work. I hope it helps you.
Or if you can try this.
let vc3 = UINavigationController(rootViewController: SearchVC())
vc3.tabBarItem.image = UIImage(named: "vyhledavani")
vc3.title = "Vyhledat"
vc3.tabBarItem.tag = 2
viewControllers = [vc3, ...]