MGSplitViewController Not As Root - ios

I need to use an MGSplitViewController because of it's ability to show the master view controller in the portrait mode. However, before displaying my split view, I need to display a login screen. Unfortunately I am unable to pop the view controller in fullscreen at startup because of some other methods that I have called! Below, is my app delegate and detail view controller codes. Please note, that the selector methods prevent me from opening a modal!
AppDelegate.h was constructed using MGSplitViewControllerAppDelegate.h
// RandomStringAppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after app launch.
// Set the split view controller as the window's root view controller and display.
//self.window.rootViewController = self.splitViewController;
// Add the split view controller's view to the window and display.
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setObject:#"YES" forKey:#"FirstRun"];
[window addSubview:splitViewController.view];
[window makeKeyAndVisible];
[rootViewController performSelector:#selector(selectFirstRow) withObject:nil afterDelay:0];
[splitViewController performSelector:#selector(toggleMasterView:) withObject:nil afterDelay:0];
[detailViewController performSelector:#selector(configureView) withObject:nil afterDelay:0];
//[self.window makeKeyAndVisible];
return YES;
}
Everything Else is Standard!
Unfortunately, I cannot pop the modal here because it crashes on me!

You could derive a class from MGSplitViewController and handle your things in viewDidLoad or viewWillAppear: in that class. So you could track your prefs key "FirstRun" and if it's set to "YES" you hide your splitview while you start your modal in viewDidLoad. I think this could do the job. btw you're missing a [prefs synchronize] in your code above, so you won't have the key written back.

Related

navController for first launch view

I currently have a launch screen storyboard with an image set up as my loading screen.
I'm used to doing everything programmatically, and storyboarding is throwing me for a ride. I have a boolean set up to determine if this is the first time the app is being launched. If so, I want to have one view controller that I set as the root of my navigation controller. If it isn't the first launch, I want to set a different root view controller to my nav controller.
I've clicked and dragged a navigation controller onto my storyboard. I just don't know what to do with it now. I have a viewController set up with an image that I'd like to be the first thing seen if it is the first time the app is being launched. However, I'm not sure what is automatically initialized using storyboard, so conceptually I'm not sure how to set up my app.
Since I have to handle this condition programmatically, do I even need to create a nav controller in storyboard, or would that be redundant?
My parent VC (viewController) is just an image with a label overlaid on top. Building with the below code in my appDelegate.h file correctly shows my launch screen storyboard, but then fades to a black screen.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
firstLaunch = YES;
if (firstLaunch == YES) {
ViewController *launchScreen = [ViewController new];
UINavigationController * navControl = [[UINavigationController alloc]initWithRootViewController:launchScreen];
self.window.rootViewController = navControl;
firstLaunch = NO;
}
else {
// create instance of other view controller and set as root of navigation controller
}
return YES;
}
Any ideas as to why my app is just navigating to a black screen?
Make sure to use
[window makeKeyAndVisible];
I usually do this in my application like so:
self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
self.navController = [[UINavigationController alloc]init];
self.window.rootViewController = self.mainViewController;
[self.window makeKeyAndVisible];
In addition, your code will always say it's the user's first time. I would recommend using NSUserDefaults as such:
BOOL hasLaunchedBefore = [[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedBefore"];
if (!hasLaunchedBefore) {
//All your first launch UI stuff
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedBefore"];
[[NSUserDefaults standardUserDefaults] synchronize];
} else {
//Not first launch
}

Change Initial View Controller for First Launch

I know this question has been asked but the methods are not working for me. I have a view controller that contains a tutorial, which I only want to display as the initial view the first time the app is opened. After that, the main menu will always be the initial view. The storyboard ID for my Tutorial screen is "Tutorial". I have the following code to detect first launch, but I can't figure out how to make the tutorial appear:
- (void)viewDidLoad
{
if ([[NSUserDefaults standardUserDefaults]boolForKey: #"FirstLaunch"])
{}
else{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"FirstLaunch"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
You can change initial controller in two ways:
Check 'Is Initial VC' checkbox at Interface Builder or
Configure it at -application:didFinishLaunchingWithOptions: method
Changing initial view controller using Application Delegate
In -application:didFinishLaunchingWithOptions: in your application delegate (AppDelegate.m) add if/else to check the necessity of tutorial:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
if (![[NSUserDefaults standardUserDefaults] boolForKey: #"FirstLaunch"])
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"FirstLaunch"];
[[NSUserDefaults standardUserDefaults] synchronize];
/*** load vc ***/
}
return YES;
}
To set the initial controller you have to initialize window property, create vc and set it as root:
// 1. Initialize window
self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
// 2. Get storyboard
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
// 3. Create vc
TutorialViewController *tutorialViewController = [storyboard instantiateViewControllerWithIdentifier:NSStringFromClass([TutorialViewController class])];
// 4. Set as root
self.window.rootViewController = tutorialViewController;
// 5. Call to show views
[self.window makeKeyAndVisible];
Hope it helps!

iOS 8 – Glitch when presenting view controller quickly after setting key window or dismissing and instanly presenting another

Since testing my app on iOS 8, I find a work around view controllers initialization and presentation really baaadly slow.
I used to work with a code similar to this on iOS 6 & 7:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
....
[self.window setRootViewController:_rootController];
[self.window makeKeyAndVisible];
// Conditions
if (#first launch condition#) {
// quite small controller containing Welcome showcase
WelcomeViewController *w = ....
[_rootViewController presentViewController:w animated:NO];
}
else if (#last opened item condition#) {
// pretty big container, root view controller contains
// a grid view which opens Item detail container the same way
ItemDetailController *item = ....
[_rootViewController presentViewController:item animated:NO];
}
}
This became a really sluggish hell with iOS 8. Root view controller now appears visible for 0.5-1 second and then instantly redraws the screen with presented one. Moreover, the slowness of the presentation began to cause an Unbalanced calls to begin/end appearance transitions _rootViewController warning.
Initial quick hint was to move both conditions with calls to another function and call it with a zero-delay so it's processed in next main run loop:
[self performSelector:#selector(postAppFinishedPresentation) withObject:nil afterDelay:0];
or something like that. This fixes the unballanced calls issue, but the visual gap (rootviewcontroller, gap, presented one) becomes (obviously) even bigger.
The slowness of the presentation is also obvious when you call something usual as:
// Example: Delegate caught finished Sign In dialog,
// dismiss it and instantly switch to Profile controller
-(void)signInViewControllerDidFinishedSuccessfully
{
[self dismissViewControllerAnimated:NO completion:^{
UserProfileViewController *userProfile = ...
[self presentViewController:userProfile animated:NO];
}];
}
which should be completely fair piece of code, which used to perform direct transition without a visible flick of parent view controller on iOS 7. Now, same thing – parent flicks during the transition, even it's both handled without animation.
Does anybody face this as an issue? Any solutions? I'd love to solve this without a need to do some hilarious magic with UIWindows for each thing I need to transit flawlessly.
I am not sure the requirement is restricted to have root view controller and present anything over there.
But according to your code it's have welcome view controller thing and I think in this case, this logic is more useful.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Conditions
if (#first launch condition#) {
// quite small controller containing Welcome showcase
WelcomeViewController *w = ....
//It can be navigation or tab bar controller which have "w" as rootviewcontroller
[self.window setRootViewController:w];
}
else if (#last opened item condition#) {
// pretty big container, root view controller contains
// a grid view which opens Item detail container the same way
ItemDetailController *item = ....
//It can be navigation or tab bar controller which have "item" as rootviewcontroller
[self.window setRootViewController:item];
}
[self.window makeKeyAndVisible];
}
If you use Storyboard, why not try:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:[[NSBundle mainBundle].infoDictionary objectForKey:#"UIMainStoryboardFile"] bundle:[NSBundle mainBundle]];
ViewController *_rootController = [storyboard instantiateViewControllerWithIdentifier:#"root"];
[self.window setRootViewController:_rootController];
[self.window makeKeyAndVisible];
if (vcToShow == 1) {
ViewController2 *w = [storyboard instantiateViewControllerWithIdentifier:#"vc2"];
[_rootController presentViewController:w animated:NO completion:nil];
}
else if (vcToShow == 2) {
ViewController2 *w = [storyboard instantiateViewControllerWithIdentifier:#"vc3"];
[_rootController presentViewController:w animated:NO completion:nil];
}
It looks like there is no delay here.
The delay I had from a dismiss/present pair was fixed by this. It may or may not help your instance. I had to completely change my strategy of displaying/dismissing modal view controllers under iOS 8:
[_rootViewController presentViewController:vc1 animated:NO completion:nil];
if(iNeedToDisplayVC2) {
[vc1 presentViewController:vc2 animated:NO completion:nil];
}
So once I am done with vc2 later on, I dismiss both vc1 and vc2 in the same call. This strategy works under iOS 7 as well. I assume earlier versions too but I did not test them.
With iOS8, I've found the old presentation is not quite completed yet in the completion block, and calling dismiss or present right away can lead to console messages and sometimes the present/dismiss does not even occur. I have had some success by adding a further delayed perform to the second presentation:
[self dismissViewControllerAnimated:NO completion:^{
UserProfileViewController *userProfile = ...
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self presentViewController:userProfile animated:NO];
}];
}];

Displaying View Controller on top of other when app starts does not work in ios8

I use following code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
window.rootViewController = self.viewController;
[window makeKeyAndVisible];
if (self.requireLogin){
[self.viewController presentViewController:self.loginViewController animated:NO completion:nil];
}
}
So i push loginViewController on top of viewController. This works ok on iOS prior to 8, but on iOS 8 you can see for small amount of time the viewController.
Is there simple way to present view controller on iOS 8 without showing what is behind it ?
Edit:
Noticed in log that it has also "Unbalanced calls to begin/end appearance transitions" so think ios 8 runs some animation on self.viewController. Is there way to stop it animating ?
Try this Hide Initially, then unhide it.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
window.rootViewController = self.viewController;
// Hide initially
window.rootViewController.view.hidden = YES;
[window makeKeyAndVisible];
if (self.requireLogin){
[self.viewController presentViewController:yourloginViewController animated:NO completion:^{
// Unhide now
window.rootViewController.view.hidden = NO;
}];
}
}
Based on the response , It seems to be not showing the splash screen, So my better suggestion is
As you knew the YES/No to show the login view(based on self.requireLogin), you make login view controller as rootviewcontroller of your window.
if (self.requireLogin){
window.rootViewController = yourLoginViewController;
}
else{
window.rootViewController = normalViewController;
}
Still you need to use the same strategy of yours then add one UIImageView on the normal View Controller, then present login view controller.
The option is yours:)

pushViewController when back from background

When app is back from the background, I want to push my menu viewcontroller from the navigation controller.
To test — NSString returns the title of my Navigation controller so it should work, but it does not.
It always show the view controller.
P.S. I use storyboard.
In AppDelegate.m:
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSString * test = self.window.rootViewController.title; // return title of navigation controller
MenuViewController *mvc = [[MenuViewController alloc] init];
[(UINavigationController *)self.window.rootViewController pushViewController:mvc animated:NO];
}
Download Project
Probably, you should update your UI in - (void)applicationDidBecomeActive:(UIApplication *)application method.
Update.
Also, error is because in your storyboard you present new ViewControllers as Modal, but you should Push them. Then you can return to main menu by calling
[(UINavigationController *)self.window.rootViewController popToRootViewControllerAnimated:NO];
To change presentation style choose segue in your storyboard, choose Attributes Inspector and change style to 'Push'.
Or, if you prefer Modal presentation style, call
[(UINavigationController *)self.window.rootViewController dismissModalViewControllerAnimated:YES];

Resources