I ignore storyboard and create UINavigationController in AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
window?.rootViewController = UINavigationController(rootViewController: HomeController(collectionViewLayout: layout))//make the ViewController class to be the root
return true
}
I have leftBarButton which switches to another UINavigationController or UICollectionViewController(according to your advice)
override func viewDidLoad() {
super.viewDidLoad()
let parentMenuButton = UIButton(frame: CGRect(x: 0, y: 0, width: 34, height: 34))
parentMenuButton.addTarget(self, action: #selector(self.menuButtonOnClicked), for: .touchUpInside)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: parentMenuButton)
}
#objc func menuButtonOnClicked(){
print("menuButtonOnClicked button is pressed")
}
How can I achieve this programmatically?(switch another navigation area by pressing menu button)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
self.navigationController!.pushViewController(secondViewController, animated: true)
Error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Storyboard () doesn't contain a view controller with identifier 'SecondViewController''
I create SecondViewController:
import UIKit
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
Is there a way to do it without messing with storyboard?(only programatically)
You need to call push method in your menuButtonOnClicked()
#objc func menuButtonOnClicked(){
print("menuButtonOnClicked button is pressed")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondViewController = storyboard.instantiateViewControllerWithIdentifier("SecondViewController") as SecondViewController
self.navigationController.pushViewController(secondViewController, animated: true)
}
Related
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]
}
}
I am trying to implement the side navigation menu (one that we have in Android) in iOS App.So far i tried the following:-
AppDelegate.swift :-
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var bridge: RCTBridge!
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let sb = UIStoryboard(name: "mystoryboard", bundle: Bundle.main)
let rootViewController: UIViewController =
sb.instantiateViewController(withIdentifier:
"NativeLabelSwiftViewController") as UIViewController
let navigationController = UINavigationController(rootViewController:
rootViewController)
window = UIWindow(frame: UIScreen.main.bounds)
window!.rootViewController = navigationController
window!.makeKeyAndVisible()
return true
}
The above is my AppDelegate class since it is a React Native project we have the generated code as well.Here added the following code to instantiate my rootViewClass(NativeLabelSwift) by following code :-
let sb = UIStoryboard(name: "mystoryboard", bundle: Bundle.main)
let rootViewController: UIViewController =
sb.instantiateViewController(withIdentifier:
"NativeLabelSwiftViewController") as UIViewController
NavigationLabelSwiftViewController.swift(Custom View Class) :-
class NativeLabelSwiftViewController: UIViewController{
var bridge: RCTBridge!
override func viewDidLoad() {
super.viewDidLoad()
title = "Production Tracking"
let btn2 = UIButton(type: .custom)
btn2.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
btn2.addTarget(self,
action:#selector(NativeLabelSwiftViewController.
onBtn2Clicked.(_:)), for: .touchUpInside)
let item2 = UIBarButtonItem(customView: btn2)
self.navigationItem.setLeftBarButton(item2, animated: true)
}
#IBAction func onBtn2Clicked(_ sender: UIBarButtonItem) {
if let navViewController = self.navigationController as?
NavigationController {
// navigation view controller is available
}
else{
// navigation view controller not available
}
}
}
The Navigation Controller has a UIViewController class (NativeLabelSwiftViewController) that has a button in Tab Bar.On Press of the Button i need to access the Navigation Controller method.But i am not able to get the navigation controller using the below code :-
let navViewController = self.navigationController as?
NavigationController; //this is NIL
But
let uiViewController = self.navigationController as?
UINavigationController; //this is not NIL
You are not referring your custom navigation controller in the AppDelegate. You used UINavigationController(rootViewController: rootViewController).
You if you want to use the storyBoard NavigationController, In AppDelegate use -
let nav = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NavigationController") as! NavigationController
window = UIWindow(frame: UIScreen.main.bounds)
window!.rootViewController = nav
window!.makeKeyAndVisible()
return true
Note: Don't forget to put an Identifier for the StoryBoard NavigationController.
I have to change my root view controller according to login status.
I had change my AppDelegate file and create a MainNavigationController class which extends UINavigationController.
**PROBLEM :- **
When the defined root class is loaded it gives all outlet variables as nil.
CODE
class PostalViewController: UIViewController {
#IBOutlet weak var btn_currentLocation: UIButton!
#IBOutlet weak var btn_viewAccount: UIButton!
#IBOutlet weak var in_postCode: UITextField!
let locManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Here it gives a nil on btn_currentLocation
btn_currentLocation.layer.cornerRadius = 15.0
btn_currentLocation.layer.masksToBounds = true
btn_currentLocation.setButtonGradient(colorOne: UIColor(named: "lightBlue")!, colorTwo: UIColor(named: "gradient2")!)
btn_viewAccount.layer.cornerRadius = 15.0
btn_viewAccount.layer.masksToBounds = true
// Add search button in post code text field
let searchButton = UIButton(type: .custom)
searchButton.setImage(UIImage(named: "iconSearch"), for: .normal)
searchButton.frame = CGRect(x: CGFloat(in_postCode.frame.size.width - 25), y: CGFloat(5), width: CGFloat(25), height: CGFloat(25))
searchButton.addTarget(self, action: #selector(self.actionSearch), for: .touchUpInside)
in_postCode.rightView = searchButton
in_postCode.rightViewMode = .always
}
AppDelegate.swift (only that particular function)
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = MainNavigationController()
return true
}
MainNavigationController.swift
class MainNavigationController : UINavigationController{
var isLogin : Bool?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
isLogin = UserDefaults.standard.bool(forKey: "isLogin")
print("IsLogin --->", isLogin!)
if (isLogin != nil && isLogin!){
perform(#selector(postalController), with: nil, afterDelay: 0.01)
}else{
perform(#selector(showHomeContoller), with: nil, afterDelay: 0.01)
}
}
#objc func showHomeContoller () {
let homepageController = HomePageViewController()
present(homepageController, animated: true, completion: nil)
}
#objc func postalController () {
let postalController = PostalViewController()
viewControllers = [postalController]
}
}
EDIT
I have add a screenshot below, I have to switch my root view between HomePage and Postal
I suppose you just want to show PostalViewController if user is logged. If user isn't logged you want to show just HomePageViewController. First delete these lines from app delegate, you don't need this:
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = MainNavigationController()
Now delete the whole navigation controller class. You won't need it because you will do all of this in HomePageViewController. Now in HomePageViewController swift file add this to viewDidLoad()
isLogin = UserDefaults.standard.bool(forKey: "isLogin")
print("IsLogin --->", isLogin!)
if isLogin != nil {
performSegue(withIdentifier: "segueToPostal", sender: self)
}
In the end set segue from HomePageViewController to PostalViewController
and set its identifier as segueToPostal
If you don't want to let user navigate back from Postal view controller you can just embbed PostalViewController in new NavigationController. Then just set segue from HomePageViewController to this NavigationController and set identifier.
IBOulet is set when view controller is initialized with nib file.
So you need create the view controllers using the init method below
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)
For example, if name of your xib file where you config your IBOultets the same as controllers name, you can just do next:
let postalController = PostalViewController(nibName: "PostalViewController", bundle: nil)
Here You will have controller with initialized IBOutlets.
For using story board you should init them from story board.
Try to ini controllers next way:
let sb = UIStoryboard(name: "MainStoryboard", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: "PostalViewController")
And you should set storyboard id in your story board for the controller, like on the screen below.
storyboard id
Also you can find more info how to init correctly view controllers from storyboard here
What is a StoryBoard ID and how can i use this?
Im trying to switch to a second view from a button on the first view, and do this programmatically (not using the traditional way of switching between views using a segue on the storyboard)
My current app delegate:
import UIKit
#UIApplicationMain
public class AppDelegate: UIResponder, UIApplicationDelegate {
public var window: UIWindow?
public func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = UINavigationController(rootViewController: ViewController())
return true
}
Button function in first view controller that when pressed should just switch to new view:
func signupAction (sender: UIButton!){
}
I already have the new viewcontroller file created:
class Signup: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.cyan
}
}
Basically, I'm trying to achieve the button in the first viewcontroller transitions to this signup viewcontroller.
If you are in a UINavigationController you could push the Signup view controller to the navigation stack this way:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let signUpViewController = storyboard.instantiateViewController(withIdentifier: "signUpViewController") as! Signup
navigationController?.pushViewController(signUpViewController, animated: true)
If you are not in UINavigationController and want to present it, you can do it this way:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let signUpViewController = storyboard.instantiateViewController(withIdentifier: "signUpViewController") as! Signup
self.present(signUpViewController, animated: true, completion: nil)
Note: Don't forget to set the identifier of your view controller in the storyboard.
I'm currently developing an application using iOS 10 and Swift 3
I think that I may have destroy the navigation between my controllers.
Indeed, when I try to present a new view controller, I have this warning on Xcode debugger.
Warning: Attempt to present FirstViewController on Project.StoryBoardManager whose view is not in the window hierarchy!
I have made some research but I'm not able to fix my bug.
I have this on my AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let storyboard = UIStoryboard(name:"Authentication", bundle: nil)
let vc = storyboard.instantiateInitialViewController() as UIViewController!
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()
return true
}
And this on my class to present news views
class StoryBoardManager: UIViewController{
fileprivate var appD = UIApplication.shared.delegate as! AppDelegate
func changeView(storyboardName: String){
let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
if let vc = storyboard.instantiateInitialViewController() {
vc.modalTransitionStyle = UIModalTransitionStyle.flipHorizontal
vc.modalPresentationStyle = UIModalPresentationStyle.fullScreen
//appD.window?.rootViewController = vc
present(vc, animated: true, completion: nil)
} else {
print("Unable to instantiate VC from \(storyboardName) storyboard")
}
}
override func viewDidLoad(){
super.viewDidLoad()
}
If I comment the update of rootViewController the new controller is not presented.
EDIT for #Zac Kwan
import Foundation
import UIKit
class CustomNavBar: UIView {
fileprivate let _storyBoardManager : StoryBoardManager = StoryBoardManager()
fileprivate var _currentUIViewController : UIViewController = UIViewController()
init() {
super.init(frame: CGRect(x: 0, y: 0, width:0, height:0))
}
func changeViewStoryboard(sender: UIButton!){
if (sender.tag == 0){
self._storyBoardManager.changeView(storyboardName: "View1")
} else if (sender.tag == 1) {
self._storyBoardManager.changeView(storyboardName: "View2")
} else if (sender.tag == 2) {
self._storyBoardManager.changeView(storyboardName: "View3")
} else {
self._storyBoardManager.changeView(storyboardName: "View4")
}
}
override init(frame: CGRect) {
super.init(frame: frame)
}
func createButton(title: String, posX: Double, witdh: Double, tag: Int, font: UIFont) -> UIButton {
let buttonCreated = UIButton(frame: CGRect(x: posX, y: 0, width: witdh, height: 60))
buttonCreated.setTitle(title, for: UIControlState())
buttonCreated.setTitleColor(CommonVariable.darkGrey, for: UIControlState())
buttonCreated.titleLabel!.font = font
buttonCreated.tag = tag
buttonCreated.addTarget(self, action:#selector(self.changeViewStoryboard(sender:)), for: UIControlEvents.touchUpInside)
buttonCreated.backgroundColor = UIColor.white
return buttonCreated
}
required init?(coder aDecoder: NSCoder) {
Super. Inuit (coder: decoder)
addSubview(self.createButton(title: « ChangeView », posX: 256.0, witdh: Double(self._sizeButton) - 1, tag: 1, font: UIFont(name: « Arial », size: 15)!))
addSubview(self.createButton(title: « ChangeView 2 », posX: 512.0, witdh: Double(self._sizeButton) - 1, tag: 2, font: UIFont(name: « Arial », size: 15)!))
}
}
Please try changing the code like below :
class StoryBoardManager: UIViewController{
fileprivate var appD = UIApplication.shared.delegate as! AppDelegate
func changeView(storyboardName: String){
let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
if let vc = storyboard.instantiateInitialViewController() {
vc.modalTransitionStyle = UIModalTransitionStyle.flipHorizontal
vc.modalPresentationStyle = UIModalPresentationStyle.fullScreen
//appD.window?.rootViewController = vc
appD.present(vc, animated: true, completion: nil)
} else {
print("Unable to instantiate VC from \(storyboardName) storyboard")
}
}
override func viewDidLoad(){
super.viewDidLoad()
}