can't change title navigation Controller - ios

I can't change the title of my navigationBar in the options view. I am not using Storyboards. I can't add button too.
This is my App Delegate code :
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let viewController = MenuTableViewController(nibName: nil, bundle: nil) //ViewController = Name of your controller
let navigationController = UINavigationController(rootViewController: viewController)
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
return true
}
This is my MenuTabeViewController file
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Options", style: .plain, target: self, action: #selector(handleOptions))
}
func handleOptions() {
let optionViewController = optionsViewController()
present(optionViewController, animated: true, completion: nil)
}
this is my options file
class optionsViewController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
view?.backgroundColor = UIColor.white
//I've tried 3 solutions
self.navigationItem.title = "Options"
self.title = "Options"
self.navigationBar.topItem?.title = "Options"
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
dismiss(animated: true, completion: nil)
}
}

I believe the issue is due to the fact that you're not setting up the new navigation / view controller properly when presenting it.
When presenting your new UINavigationController, as you did previously in the AppDelegate, you'll want to create a UIViewController and set it as the rootViewController. So you'll want something like OptionsNavController and OptionsViewController instead of a single nav controller.
Then in your OptionsViewController simple call self.title = #"Options".
EDIT
I'm including an example below.
func handleOptions() {
let optionsViewController = OptionsViewController()
let optionsNavController = UINavigationController(rootViewController: optionsViewController)
present(optionsNavController, animated: true, completion: nil)
}
so then as stated above, call self.title = #"Options" in OptionsViewController.

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]
}
}

UIViewcontroller disappear after switch tabs UITabBarController

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.

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.

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 = []

Moving from one UIViewController to another in Swift programmatically

I have 2 ViewControllers. I don't have a storyboard. I want to move from the first view to another once everything is done in first viewController. What is the way to do it? It is something like this, the first view shows an image and in the background makes some API calls. After API call succeeds, I want it to move to the second ViewController(LoginActivityViewController). I tried calling this in the first ViewController:
var loginActivity = LoginActivityViewController()
self.navigationController.pushViewController(loginActivity, animated: true)
But, this did not work. How to do this?
Here is my application function in AppDelegate
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.mainScreen().bounds)
let navigationController: UINavigationController = UINavigationController(rootViewController: RootViewController())
navigationController.setNavigationBarHidden(true, animated: false)
window!.rootViewController = navigationController
window!.makeKeyAndVisible()
return true
}
Here is my RootViewController's viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let image1 = UIImage(named: "Default.png")
let imageview = UIImageView(image: image1)
self.view.addSubview(imageview)
var loginActivity = LoginActivityViewController()
self.navigationController.pushViewController(loginActivity, animated: true)
}
this how your method should look with initing a navigation controller properly:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
window = UIWindow(frame: UIScreen.mainScreen().bounds)
let rootViewController: RootViewController = RootViewController(nibName: "RootViewController", bundle: nil)
let navigationController: UINavigationController = UINavigationController(rootViewController: rootViewController)
window!.rootViewController = navigationController
window!.makeKeyAndVisible()
return true
}
and now you are able to push new view controllers into the hierarchy.
update
if you don't want to show the navigation bar insert this line into the code above.
navigationController.setNavigationBarHidden(true, animated: false)
var next = self.storyboard?.instantiateViewControllerWithIdentifier("DashboardController") as! DashboardController
self.presentViewController(next, animated: true, completion: nil)
don't forget to set ViewController StoryBoard Id in StoryBoard -> identity inspector

Resources