I wonder why my managedObjectContext is nil (in my TableViewController class) when i pass it like that in app delegate:
// Fetch Main Storyboard
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
// Instantiate Root Navigation Controller
UINavigationController *rootNavigationController = (UINavigationController *)[mainStoryboard instantiateViewControllerWithIdentifier:#"rootNavigationController"];
// Configure View Controller
TableViewController *viewController = (TableViewController *)[rootNavigationController topViewController];
if ([viewController isKindOfClass:[TableViewController class]]) {
[viewController setManagedObjectContext:self.managedObjectContext];
NSLog(#"Saved");
}
And in console i could see "Saved" output, but when i log managedObjectContext in my class its nil..
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
self.managedObjectContext = app.managedObjectContext;
Did solve problem, but i still want to know why previous solution not work?
The reason is that very likely the view controller you are creating in code is actually overwritten with the one sent directly by the storyboard. There is no good reason to create a navigation and view controller from storyboard like this in the app delegate.
So your solution is the correct one and also quite common to have the view controller get its context from a singleton or the app delegate. Keep this pattern and stop worrying about the other problem.
Related
I have some logic that requires a different view to appear once a button is pressed. This code bellow works from the appDelegate but not in my ViewController implementation. Is there a way to change the view controller storyboard from the current ViewController implementation?
self.window.rootViewController =
(UIViewController *)[[UIStoryboard storyboardWithName:#"Main" bundle: nil] instantiateViewControllerWithIdentifier:#"homeView"];
Along as the controller's view is currently on screen, it has a non-nil window property that points to its window. Also, if the controller with this code was made in the same storyboard, you can just use self.storyboard instead of getting a storyboard from its name.
self.view.window.rootViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"homeView"];
You can get the window from the UIApplication instance:
UIApplication *application = [UIApplication sharedApplication];
UIViewController *newRoot = [self.storyboard instantiateViewControllerWithIdentifier:#"homeView"];
[(AppDelegate *)application.delegate window].rootViewController = newRoot;
Apple documentation says that any application using a split view controller should make it as a root view controller. But i am struck at a state where, my login screen should redirect me to a split view controller. Is there a way to achieve this?
I am Using storyboards and new to programming. Kindly help.
One quite common way to solve this issue to change the rootViewController of your applications main UIWindow (which again is a property of your AppDelegate) after a successful login.
So, the initial view controller of your app needs to be your LoginViewController that handles the login. After a successful login, you can do something like this:
- (void)switchToMainInterface
{
// Change the root view controller of the application window to the main storyboard
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
UISplitViewController *mainSplitViewController = [mainStoryboard instantiateViewControllerWithIdentifier:#"MainSplitViewController"];
UIWindow *mainApplicationWindow = [[[UIApplication sharedApplication] delegate] window];
mainApplicationWindow.rootViewController = mainSplitViewController;
}
Note that this code is just dummy code to make my suggestion a bit more tangible, it makes the following assumptions:
you have a Storyboard called Main in your application bundle
within this Main Storyboard you have a UISplitViewController with the Storyboard ID MainSplitViewController so that you can instantiate it programmatically
you need to import AppDelegate.h to get access to the root UIWindow
Link your LoginViewController to a UIViewcontroller. In this controller drag an UIContainerView, and embed your UISplitViewController in it.
I created a custom segue class and implemented the following code. I am not sure what it does to my application. It seems a bit high level code to me as I am an amature but it works fine. Hope you find it useful.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//Override point for customization after application launch.
// UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
// UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
// splitViewController.delegate = (id)navigationController.topViewController;
return YES;
}
Commented the above code, I believe this is to pause UISplitViewController from loading into UIWindow.
And my custom segue --> segue.m is as follows..
#import "Seague.h"
#implementation Seague
-(void)perform
{
UIViewController *sourceViewController = (UIViewController *)self.sourceViewController;
UIViewController *destinationViewController = (UIViewController *)self.destinationViewController;
UISplitViewController *splitViewController = (UISplitViewController *)destinationViewController;
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
splitViewController.delegate = (id)navigationController.topViewController;
UIWindow *window = [UIApplication sharedApplication].keyWindow;
window.rootViewController = destinationViewController;
window.rootViewController = sourceViewController;
[UIView transitionWithView:sourceViewController.view.window duration:0.5 options:UIViewAnimationOptionTransitionNone animations:^{
window.rootViewController = destinationViewController;
} completion:^(BOOL finished){}];
}
#end
This segue is triggered when my login button is pressed and the login details are valid.
My rootViewController is the viewController that has my login button and not UISplitViewController.
Reference: This is not a code I wrote. Found it somewhere on web after searching for 2 days. Will update the source link for reference soon.
Thankyou all for your responses :)
I'm developing storyboard based app and I'm wondering if I could have 2 TabBarControllers - one is for user not logged in and another one for user logged in. Can I swap NotLoggedInTabBarController to LoggedInTabBarController on login button action?
If you add a Restoration ID to your viewControllers in the storyboard (identity inspector) you can instantiate a controller by calling the method instantiateViewControllerWithIdentifier:
In your case, maybe i would have instantiate a parent view controller, and when wanting to swap of tabBarcontroller, I would have called
[parentViewController.storyboard instantiateViewControllerWithIdentifier:#"tabBarController2"];
And then change controller displayed.
(Not tested, just some ideas...)
You can add IBAction to your button with check of this condition and instantiate needed controller.
ViewController *viewController = nil;
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
YourViewController *viewController = nil;
if ([userManager isLoggedIn]) {
viewController = (YourViewController *)[storyboard instantiateViewControllerWithIdentifier:#"LoggedInViewControllerIdentifier"];
} else {
viewController = (YourViewController *)[storyboard instantiateViewControllerWithIdentifier:#"NotLoggedInViewControllerIdentifier"];
}
[self.navigationController pushViewController:viewController animated:YES];
in your storyboard you'll have to set storyboard id in Identity inspector
EDIT
If you are not using navigationController to route to correct logged/notlogged controllers then your could do sth like that:
MyAppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate.transitionController transitionToViewController:viewController
withOptions:UIViewAnimationOptionTransitionFlipFromRight];
I will try to explain this as best as I can.
My application has a TabBarController which functions as the main navigation
I have a modal view that I segue to to add a list. that screen can be reached from 2 different viewcontrollers.
From the main route I simple just close the modal and all is fine. However from the second route I need to be able to open up an entirely new ViewController.
The issue that I am having is that I can not seem to open that ViewController with the TabBar and NavBar included.
This is the code I am currently playing with to try to get it to work.
UITabBarController *tabController = [self.storyboard instantiateViewControllerWithIdentifier:#"MainInterface"];
tabController.selectedIndex = 1;
//_window.rootViewController = tabController;
UINavigationController *groceryNavController = [self.storyboard instantiateViewControllerWithIdentifier:#"MainNavController"];
UIViewController *groceryViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"GroceryViewController"];
UIViewController *currentVC = self;
[currentVC.navigationController pushViewController:groceryViewController animated:YES];
One way to to do it is through the delegate. If in the delegate, the relevant navigation controller is called:
self.navigationController
Then you would have to do:
YourAppDelegate *delegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate.navigationController pushViewController:groceryViewController animated:YES];
(replace "YourAppDelegate" with the actual name of your app delegate)
I have a view controller subclass, SignInViewController, used for sign in that might be needed at any time. Rather than have every view controller in my app listen for the notification that sign in is needed, I'd rather have the app delegate do it.
But how do I trigger it from my app delegate?
Do I put the SignInViewController in my main storyboard? If so, how do I access my storyboard from my app delegate? Or is some other approach better?
You can always reference to your app delegate through the UIApplication singleton.
From there you can always get your root view controller.
With your root view controller you can get a reference to the storyboard.
Once you have your story board all you do is instantiate an instance of the view controller you want.
Present it.
AppDelegate* appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
MainViewController *mvc = (MainViewController *)appDelegate.window.rootViewController;
LoginViewController *lvc = [mvc.storyboard instantiateViewControllerWithIdentifier:#"LoginViewController"];
[currentVC presentModalViewController:lvc animated:YES];
There may be a more direct way of getting a reference to your storyboard but this will almost always get it for you.
To show a view controller from anywhere (including the app delegate), I have used this code with success in iOS 8+ (I'm not sure about earlier compatibility). It will present from the modal view if there is one.
YOURAppDelegate *appDelegate = (YOURAppDelegate *)[[UIApplication sharedApplication] delegate];
UINavigationController *rootNavC = (UINavigationController *)appDelegate.window.rootViewController;
UIViewController *topVC = rootNavC.topViewController;
UIViewController *myNewVC = [rootNavC.storyboard instantiateViewControllerWithIdentifier:<YOUR STORYBOARD ID>];
if (topVC.presentedViewController)
{
if ([topVC.presentedViewController class] == [UINavigationController class])
{
dispatch_async(dispatch_get_main_queue(), ^{
[((UINavigationController*)topVC.presentedViewController) pushViewController:myNewVC
animated:YES];
});
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
[topVC.presentedViewController.navigationController pushViewController:myNewVC
animated:animated];
});
}
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
[rootNavC pushViewController:myNewVC
animated:animated];
});
}
I usually wrap this in a convenience method and pass in my instantiated view controller. To OP - you would create SignInViewController in the storyboard, assign it a unique storyboard ID, and then substitute that ID in the above code.