How to represent a startup screen in Storyboards - ios

Right now I have a MainViewController which is embedded in a UINavigationController so the navigation controller is set as initial view controller.
What I want is first time the app starts and only then to show a StartupViewController. But I am not sure how to represent this in my storyboard.
Should this be set as initial view controller if the app starts the first time, if not the navigation controller should be set.
What I tried is to show StartupViewController in MainViewController::viewDidLoad(), but its not working how I wanted too.
Anyone know what is the right approach to do this ?

Hello this code may be helpfull for you :
// MARK: - Application Supporting Funcations -
func loadFirstViewController(viewController: String)
{
// for loading first view Controller
let storyBoard = appDelegate.AppStoryBoard()
/////////////////////////Add Navigation Controller and set Root View Controller///////////////////////////////////////
let object = storyBoard.instantiateViewControllerWithIdentifier(viewController) // for prelogin navigation
self.navigationController = UINavigationController(rootViewController: object)
self.navigationController?.navigationBar.hidden = true
self.loadNavigationControllerToWindow()
}
func loadNavigationControllerToWindow()
{
if self.window == nil
{
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
}
// for loading root view
self.window!.rootViewController = self.navigationController!
self.window!.makeKeyAndVisible()
}
call this function in didFinishLaunchingWithOptions
if((NSUserDefaults.standardUserDefaults().valueForKey("isFirstTime") != nil))
{
self.loadFirstViewController("LoginVC") // for loading first view controller
}
else
{
self.loadFirstViewController("WelcomeVC") // for loading first view controller
}
put this after you go in second controler viewDidLoad
if((NSUserDefaults.standardUserDefaults().valueForKey("isFirstTime") == nil))
{
NSUserDefaults.standardUserDefaults().setValue("1", forKey: "isFirstTime")
}

This is how I acheived in my app.
Add this in appdelegate applicationdidfinishlaunching.
NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];
if (![defaults boolForKey:#"FirstTime"])
{
//NSLog(#"Enter First Time ");
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]];
StartupViewController * firstViewController = [storyBoard instantiateViewControllerWithIdentifier:#"StartupViewController"];//AskAndAnswerViewId RegisterViewId TagViewId HelpViewId
self.navigationController=[[UINavigationController alloc]initWithRootViewController:firstViewController];
self.window.rootViewController = self.navigationController;
[self.navigationController setNavigationBarHidden:YES];
}
else
{
//NSLog(#"not First Time");
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]];
HelpViewController *mainviewcontroller = [storyBoard instantiateViewControllerWithIdentifier:#"AskAndAnswerViewId"];
self.navigationController=[[UINavigationController alloc]initWithRootViewController:mainviewcontroller];
self.window.rootViewController = self.navigationController;
[self.navigationController setNavigationBarHidden:YES];
}
Once you reached in Mainviewcontroller change the userdefault value for FirstTime to "NO".

I understand your Question ,
In AppDelegate try to set rootViewController ,
I have store data in the NSUserDefault if user came first time
And try to set rootViewController once he visit first time.
In below Example I have navigate user to Login screen/Home screen
let str = userDefaults.objectForKey("firstTime")
{}else{}

Please don't forget to give identifier to your view controller StartupViewController
-> user interaction enable in StartupViewController
if (UserDefaults.standard.string(forKey: "keepUsername") != nil) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "Revealview")
self.present(vc, animated: false, completion: nil)
}
else
{
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "loginview") as! loginview
self.present(nextViewController, animated:true, completion:nil)
}

Related

How to reset root view controller

Is it possible to reset the root view controller? With reset I mean resetting it to its initial state so viewDidLoad will be called again. I'm using a UITabBarController and when I logout I want all the tabs previously loaded to be unloaded.
You can do this by setting the instance of TabBarController to rootViewController on logout action.
Swift 3:
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let tabBarController = storyBoard.instantiateViewController(withIdentifier: "TabBarController") as! UITabBarController
UIApplication.shared.keyWindow?.rootViewController = tabBarController
UIApplication.shared.keyWindow?.makeKeyAndVisible()
Objective C:
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UITabBarController *tabBarController = [storyBoard instantiateViewControllerWithIdentifier:#"TabBarController"];
[[[UIApplication sharedApplication] keyWindow] setRootViewController:tabBarController];
[[[UIApplication sharedApplication] keyWindow] makeKeyAndVisible];
If you are using navigation controller on Tabbarcontroller then navigate to that navigation controller otherwise go to Tabbarcontroller as-
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let tabBar = mainStoryboard.instantiateViewControllerWithIdentifier("TabBarController") as! TabBarController
appDelegate.window?.rootViewController = tabBar
appDelegate.window?.makeKeyAndVisible()
Set view property of UIViewController to nil
UIApplication.shared.keyWindow?.rootViewController?.view = nil
it will force UIViewController to init his life cycle from a beginning after next call to self.view

Black screen when changing root view controller in AppDelegate

I am trying to change the root view controller from the app delegate's didFinishLaunchingWithOptions, depending on whether the user is logged in or not. Once I get past this condition, I am using the following code to change root view controllers:
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SWRevealViewController") as! SWRevealViewController
self.window?.makeKeyAndVisible()
However, when I launch the app (with a valid logged in user) the simulator first shows the log in screen (old root view controller) for a second, then the screen goes black for about 30 seconds to a minute, before finally showing the desired view controller.
The view controller structure in storyboard is as follows:
SWRevealViewController -> Navigation Controller -> Desired View Controller (new root)
The reason for beginning with SWRevealViewController is because the slide menu is lost otherwise.
Any ideas of what might be going on?
I found a way to produce a similar result to the one I desired. It involves not changing the root view controller at all, and having it present an "artificial root view controller" after launch:
if let currentRoot = self.window?.rootViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let artificialRoot = storyboard.instantiateViewController(withIdentifier: "SWRevealViewController")
currentRoot.present(artificialRoot, animated: false, completion: nil)
}
Although not ideal, the results of this implementation are by far better than the implementation in the question description.
Here is an example, that works for me, just make your loginViewControllers to be a stack of UINavigationController, not related to SWRevealController. It is easy workaround.
self.window = UIWindow(frame: UIScreen.main.bounds)
if User.shared.firstLaunch {
let navigationVC = Storyboard.inital.instantiateViewController(withIdentifier: "firstLaunchNC") as! UINavigationController
self.window?.rootViewController = navigationVC
} else if User.shared.token == "" {
let navigationVC = Storyboard.inital.instantiateViewController(withIdentifier: "initialVC") as! UINavigationController
self.window?.rootViewController = navigationVC
} else {
self.registerForPushNotifications(application: UIApplication.shared)
User.shared.refreshToken()
let revealController = Storyboard.main.instantiateViewController(withIdentifier: "revealViewController") as! SWRevealViewController
self.window?.rootViewController = revealController
}
self.window?.makeKeyAndVisible()
return true
try this:
let viewController = ViewController()
let navigationController = UINavigationController(rootViewController: viewController)
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
try this:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UITabBarController *tbc = [storyboard instantiateViewControllerWithIdentifier:#"TabBarController"];
tbc.selectedIndex=[singleton getSelectedTabIndex];
// tbc.selectedIndex=0;
//self.window.rootViewController = tbc;
[self.window setRootViewController:tbc];
[UIView transitionWithView:self.window duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve animations:nil
completion:nil];
If This not work..Please check your initial Controller ...In Which Never try Load NSAtrributedString
NSAttributedString *attrStr = [[NSAttributedString alloc]
initWithData:[htmlString dataUsingEncoding:NSUnicodeStringEncoding]
options:#{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: #(NSUTF8StringEncoding)}
documentAttributes:nil
error:nil];
Note:-- Black Screen Apear When Initial Controller ViewDidLoad Take Some time to load UIview into Memory due some Lazy UI loading Assests are done in ViewDidLoad...
Try heavy load view cide into ViewWillApear or ViewDidApear ....

Going directly to Main View controller for already logged in user

I have a loginViewController as my rootviewcontroller followed by the main screen and then other screens. My views follow push and pop approach. What I want is, if a user is already logged in my view should start from main screen else start from login screen, and if I logout from main screen it should go back to login screen and the push and pop structure should be maintained. I can achieve this using the modal transition, but I need to use push and pop approach, is this possible? Currently I have checked a already logged in condition in my appdelegate to set the rootview controller but it fails if I attempt to log out as its not present in my navigation controller stack.
if !alreadyLoggedin
{
let mainListVC = storyBoard.instantiateViewControllerWithIdentifier(“MainListViewController”)
self.window!.rootViewController = mainListVC
}
else
{
let loginVC = storyBoard.instantiateViewControllerWithIdentifier("ViewController")
self.window!.rootViewController = loginVC
}
It's basic use case, Please follow my steps.
In your story create navigation view controller with view controller.
Select navigation view controller as your initial view controller in storyboard.
Create three views controllers- InitialViewController, LoginViewController and MainViewController and set storyboard id for all view controllers.
Created sample project for you. Download link (https://www.dropbox.com/s/zk8x7ptg5mzmotk/test.zip?dl=0)
You need to use. AppDelegate
Objective C
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"LoginStatus"]) {
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
self.window.rootViewController = [storyboard instantiateViewControllerWithIdentifier:#"loginController"];
}else{
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
self.window.rootViewController = [storyboard instantiateViewControllerWithIdentifier:#"RootView"];
}
}
SWIFT - i hope its correct
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
//Using UserDefaults check already loggedin user or not
if (!someStatus) {
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("someViewController") as! UIViewController
self.presentViewController(vc, animated: true, completion: nil)
}else{
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("loginViewController") as! UIViewController
self.presentViewController(vc, animated: true, completion: nil)
}
}

Programmatically change rootViewController of storyBoard

I have created my project using Storyboards. The root ViewController lies inside a Storyboard, I have not written a single code in the appDelegate.
Now I want to show a tour of my app, so I want to change the root ViewController from Tab Bar to my TourVC and when the tour of the app is finished , I want to again switch back my root ViewController to Tab Bar.
So I looked up online and followed the following points
1) Remove Storyboards from app.plist file,
2) Uncheck option "isInitialViewController" from Storyboards which is checked in case of Tab Bar controller because its a root ViewController,
3) Add this code in appDelegate.m file.
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
ProductTourViewController *PT = [[ProductTourViewController alloc] initWithNibName:#"ProductTourViewController" bundle:nil];
self.window.rootViewController = PT;
[self.window makeKeyAndVisible];
return YES;
But my app crashes with this error log,
[ProductTourViewController selectedViewController]: unrecognized selector sent to instance 0x1766a9e0
And also I get a warning,
Unsupported Configuration: Scene is unreachable due to lack of entry points and does not have an identifier for runtime access via -instantiateViewControllerWithIdentifier:.
Objective-C:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UITabBarController *rootViewController = [storyboard instantiateViewControllerWithIdentifier:#"tabBarcontroller"];
[[UIApplication sharedApplication].keyWindow setRootViewController:rootViewController];
Swift :
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = mainStoryboard.instantiateViewControllerWithIdentifier("tabBarcontroller") as UITabBarController
UIApplication.sharedApplication().keyWindow?.rootViewController = viewController;
Swift 3:
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = mainStoryboard.instantiateViewController(withIdentifier: "tabBarcontroller") as! UITabBarController
UIApplication.shared.keyWindow?.rootViewController = viewController
Swift 5:
let viewController = mainStoryboard.instantiateViewController(withIdentifier: "tabBarcontroller") as! UITabBarController
UIApplication.shared.windows.first?.rootViewController = viewController
UIApplication.shared.windows.first?.makeKeyAndVisible()
Or simply like this:
let viewController = mainStoryboard.instantiateViewController(withIdentifier: "tabBarcontroller") as! UITabBarController
self.view.window?.rootViewController = viewController
self.view.window?.makeKeyAndVisible()
Both works fine!
Set storyboard ID for your class in your main storyboard.
UIStoryboard *MainStoryboard = [UIStoryboard storyboardWithName:#"Main"
bundle: nil];
UINavigationController *controller = (UINavigationController*)[MainStoryboard
instantiateViewControllerWithIdentifier: #"RootNavigationController"];
LoginViewController *login=[MainStoryboard instantiateViewControllerWithIdentifier:#"LoginViewController"];
[controller setViewControllers:[NSArray arrayWithObject:login] animated:YES];
self.window.rootViewController=controller;
In swift we can implement it is as following
let storyboard = UIStoryboard(name: "StartingPage", bundle: NSBundle.mainBundle())
let loginView: SignInVC = storyboard.instantiateViewControllerWithIdentifier("SignInVC") as! SignInVC
UIApplication.sharedApplication().keyWindow?.rootViewController = loginView
I use simple this:
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"NameOfStoryBoard" bundle:nil];
UITabBarController *rootViewController = [sb instantiateViewControllerWithIdentifier:#"NameOfTabBarController"];
[[UIApplication sharedApplication].keyWindow setRootViewController:rootViewController];
Just to add to Sunny Shah's answer, this is the Swift 3 version of it:
let mainStoryBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewController: UIViewController = mainStoryBoard.instantiateViewController(withIdentifier: "MainTabBarController") as! UITabBarController
UIApplication.shared.keyWindow?.rootViewController = viewController
Swift 3 code:
Use below in didFinishLaunchingWithOptions Appdelegate function.
Replace "HomeViewController" with ViewController you want to set as Root ViewController on app launch.
self.window = UIWindow(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewController(withIdentifier: "HomeViewController")
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
return true
Swift 5 + Xcode 11:
Make like this:
let viewController = mainStoryboard.instantiateViewController(withIdentifier: "tabBarcontroller") as! UITabBarController
UIApplication.shared.windows.first?.rootViewController = viewController
UIApplication.shared.windows.first?.makeKeyAndVisible()
Or like this:
let viewController = mainStoryboard.instantiateViewController(withIdentifier: "tabBarcontroller") as! UITabBarController
self.view.window?.rootViewController = viewController
self.view.window?.makeKeyAndVisible()
Both works fine!
Objective c
step 1: remove main story board from info.plist
step 2: add storyboard id to your view controller in your interface builder
step 3: add the following code to application did finish method in app delegate
self.window = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds];
//set main story board
if( condition){
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"StoryboardName1" bundle:nil];
UIViewController *rootViewController = [storyboard instantiateViewControllerWithIdentifier:#"ViewController1"];
[[UIApplication sharedApplication].keyWindow setRootViewController:rootViewController];
[self window].rootViewController = rootViewController;
[self.window makeKeyAndVisible];
}else{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"StoryboardName2" bundle:nil];
UIViewController *rootViewController = [storyboard instantiateViewControllerWithIdentifier:#"ViewController2"];
[[UIApplication sharedApplication].keyWindow setRootViewController:rootViewController];
[self window].rootViewController = rootViewController;
[self.window makeKeyAndVisible];
}
This is an old article, but I will reply.
I do not recommend the following code.
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = mainStoryboard.instantiateViewController(withIdentifier: "tabBarcontroller") as! UITabBarController
UIApplication.shared.keyWindow?.rootViewController = viewController
Because you are creating two instances.
I recommend writing the following code in the appropriate ViewController.
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = self

How can I load storyboard programmatically from class?

My problem is that I was looking for way to use both storyboard and xib. But I can't find proper way to load and show storyboard programmatically. Project was started developing with xib, and now it's very hard to nest all xib files in storyboard. So I was looking a way to do it in code, like with alloc, init, push for viewControllers. In my case I have only one controller in storyboard: UITableViewController, which has static cells with some content I want to show. If anyone knows proper way to work both with xib and storyboard without huge refactoring, I will appreciate for any help.
In your storyboard go to the Attributes inspector and set the view controller's Identifier. You can then present that view controller using the following code.
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UIViewController *vc = [sb instantiateViewControllerWithIdentifier:#"myViewController"];
vc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:vc animated:YES completion:NULL];
Swift 3
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "viewController")
self.navigationController!.pushViewController(vc, animated: true)
Swift 2
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("viewController")
self.navigationController!.pushViewController(vc, animated: true)
Prerequisite
Assign a Storyboard ID to your view controller.
IB > Show the Identity inspector > Identity > Storyboard ID
Swift (legacy)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("viewController") as? UIViewController
self.navigationController!.pushViewController(vc!, animated: true)
Edit: Swift 2 suggested in a comment by Fred A.
if you want to use without any navigationController you have to use like following :
let Storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = Storyboard.instantiateViewController(withIdentifier: "viewController")
present(vc , animated: true , completion: nil)
In attribute inspector give the identifier for that view controller and the below code works for me
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
DetailViewController *detailViewController = [storyboard instantiateViewControllerWithIdentifier:#"DetailViewController"];
[self.navigationController pushViewController:detailViewController animated:YES];
Try this
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc = [mainStoryboard instantiateViewControllerWithIdentifier:#"Login"];
[[UIApplication sharedApplication].keyWindow setRootViewController:vc];
in swift
NavigationController and pushController you can replace for
present(vc, animated:true , completion: nil)
For swift 4 and 5, you can do this. Good practice is set name of Storyboard equal to StoryboardID.
enum StoryBoardName{
case second = "SecondViewController"
}
extension UIStoryboard{
class func load(_ storyboard: StoryBoardName) -> UIViewController{
return UIStoryboard(name: storyboard.rawValue, bundle: nil).instantiateViewController(withIdentifier: storyboard.rawValue)
}
}
and then you can load your Storyboard in your ViewController like this:
class MyViewController: UIViewController{
override func viewDidLoad() {
super.viewDidLoad()
guard let vc = UIStoryboard.load(.second) as? SecondViewController else {return}
self.present(vc, animated: true, completion: nil)
}
}
When you create a new Storyboard just set the same name on StoryboardID and add Storyboard name in your enum "StoryBoardName"
You can always jump right to the root controller:
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc = [storyboard instantiateInitialViewController];
vc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:vc animated:YES completion:NULL];
The extension below will allow you to load a Storyboard and it's associated UIViewController. Example: If you have a UIViewController named ModalAlertViewController and a storyboard named "ModalAlert" e.g.
let vc: ModalAlertViewController = UIViewController.loadStoryboard("ModalAlert")
Will load both the Storyboard and UIViewController and vc will be of type ModalAlertViewController. Note Assumes that the storyboard's Storyboard ID has the same name as the storyboard and that the storyboard has been marked as Is Initial View Controller.
extension UIViewController {
/// Loads a `UIViewController` of type `T` with storyboard. Assumes that the storyboards Storyboard ID has the same name as the storyboard and that the storyboard has been marked as Is Initial View Controller.
/// - Parameter storyboardName: Name of the storyboard without .xib/nib suffix.
static func loadStoryboard<T: UIViewController>(_ storyboardName: String) -> T? {
let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
if let vc = storyboard.instantiateViewController(withIdentifier: storyboardName) as? T {
vc.loadViewIfNeeded() // ensures vc.view is loaded before returning
return vc
}
return nil
}
}

Resources