Pushing ViewController from app delegate - ios

I am using a storyboard in Xcode 5, which appears as so:
My requirement is to push a ViewController (VIEW1 or VIEW2) into view from the app delegate. Essentially it should not matter what view is presently on the screen -- I would just like to make a ViewController appear when the app delegate picks up an external event.
In order to try and achieve this, I have property references to both the TabBarCtrl-Products and NavCtrl-ProductA in my app delegate.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
_tabBarProducts = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"sidTabBarProducts"];
NSArray *tabvcs = _tabBarProducts.viewControllers;
for (id controller in tabvcs){
if ([controller isKindOfClass:[VCNavControl_ProductA class]]) {
_navControllerProductA = controller;
break;
}
}
return YES;
}
The app delegate method to push VIEW2 is:
-(void)showVCVIEW2
{
VC_V2 *targetvc = nil;
targetvc = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"sidView2"];
[[AppDelegate sharedInstance].navControllerProductA pushViewController:targetvc animated:NO];
}
This works OK when VIEW1 is showing at the time showVCVIEW2 is called, however it does not work when ViewCtrl-ProductB is showing. I can see that the new instance of targetvc has been added to the AppDelegate _navControllerProductA's stack, however it does not display.
(Regarding the setting of the the app delegate's rootViewController, I set this to _tabBarProducts after the VC-Splash and VC-Setup ViewCtrls have finished).
I would appreciate very much if anyone can give me an idea on how to achieve this. I suspect my problems stem from having a NavCtrl in a TabBarCtrl, but I do not know a way around this.

Your problem is that the navigation controller you are pushing on is not in the view hierarchy.
You instead could try setting the tabBarController's selected index like this:
[self.tabBarController setSelectedIndex:1];

Related

MMDrawerController: pushing viewcontroller form application open URL

I've got an app that is a list of itens in a table view and displays a detail view controller to every item on the table view. It also implements MMDrawerController (root view controller) as a side menu with storyboard.
I'm deep linking my app and using application open URL source application annotation method from App Delegate to handle it. So, I want to push a detail view controller from this method using MMDrawerController and I'm having some trouble.
Have a look in some code:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
DetalheRestauranteViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"detalheRest"];
MMDrawerController* drawerController = (MMDrawerController *)self.window.rootViewController;
// If I use this nothing happens
[drawerController.centerViewController.navigationController pushViewController:vc animated:YES];
// If I use this nothing happens
[((MMDrawerController *)self.window.rootViewController).centerViewController.navigationController pushViewController:vc animated:YES];
// If I use this I got the unrecognized selector error
[(UINavigationController*)self.window.rootViewController pushViewController:vc animated:NO];
return YES;
}
Can someone help me with this?
I have found that with deep linking and the new iOS9 menu shortcuts that a small delay is often needed to display or manipulate UI Components.
I would try moving all of your UI/MMDrawer code to its own method. Then when openURL is called in your app delegate, call your new method on a delay.
So in ObjC it would look something like this:
[self performSelector:#selector(showDetailView) withObject:nil afterDelay:0.3];
do you solve this problem?
I tried many times. Finally I found it works fine for me today:
[((UINavigationController *)((MMDrawerController *)self.window.rootViewController).centerViewController) pushViewController:viewController animated:YES];
or
MMDrawerController *mvc = (MMDrawerController *)self.window.rootViewController;
UINavigationController *nvc = (UINavigationController *)mvc.centerViewController;
[nvc pushViewController:vc animated:YES];
These two kinds of writing are the same.
Here vc is one DetalheRestauranteViewController instance.

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

App desing: UITabBarController inside UINavigationController

My app has to have a launch screen where I perform some operations such as updating web content. After the process is done, I display the whole app interface within a UITabBarController. At some point, the app has to go back to this launch view controller to handle the update of the application data.
Apple specifically states that a UITabBarController should be the root view controller of any app.
I'm looking for clever ways of presenting a UIViewController before a UITabBarController without embedding both of them in a UINavigationController.
I currently have the setup I want to avoid (UINavigationController -> UITabBarController) because it works and makes sense. I'm afraid Apple wont like it, so i'm looking forward for some light in the subject.
However, nothing that I've read says that the root controller has to remain the same throughout the life of the app. What about something like...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.tabController = (UITabBarController *)[self.window rootViewController];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
self.altController = [storyboard instantiateViewControllerWithIdentifier:#"AlternateController"];
return YES;
}
- (void)swapRootControllers {
if ([[self.window rootViewController] isKindOfClass:[UITabBarController class]]) {
self.window.rootViewController = self.altController;
} else {
self.window.rootViewController = self.tabController;
}
}
...assuming all the supporting variable declarations and storyboard implementation.

How to display only UIview IOS7?

Hi in storyboard i have added a new view controller object inside the view controller i have a small registration from with only two fields example like name and phone number. In that when user open the application i want display only the newly added UIview.
But the problem is uiview is showing with the storyboard please tell me how to display only the uiview. Is that possible to do ? possible means please tell how to make it done. I'm navigating the view from my app-delegate.
App delegate code.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIStoryboard *mainstoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
buttonpoliticalViewController *ringingVC = [mainstoryboard instantiateViewControllerWithIdentifier:#"Upcv"];
[self.window setRootViewController: ringingVC];
return YES;
}
In above code i have used the storyboard id to view the view controller please tell how to display only uiview.
Thanks.

Setting up a UINavigation Controller on a UIViewController as rootView

I had a tabBarApplication that I have as a template which has a UINavigation template on each tab.
I want to use that sample (sort of) and convert it into a single UIViewController in a nother application. I have presented 2 pieces of code, the first one is my template and the latter is what I am trying to make. Could anyone please give me some hints or help with how to that right? I keep getting errors, but they do not make sense to me as the tabBarApp does not need it a viewController declared.
First Code (the example):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self setupFetchedResultsController];
if (![[self.fetchedResultsController fetchedObjects] count] > 0 ) {
NSLog(#"!!!!! ~~> There's nothing in the database so defaults will be inserted");
[self importCoreDataDefaultRoles];
}
else {
NSLog(#"There's stuff in the database so skipping the import of default data");
}
// The Tab Bar
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
// The Two Navigation Controllers attached to the Tab Bar (At Tab Bar Indexes 0 and 1)
UINavigationController *personsTVCnav = [[tabBarController viewControllers] objectAtIndex:0];
UINavigationController *rolesTVCnav = [[tabBarController viewControllers] objectAtIndex:1];
return YES;
}
Second Code (what I am trying to make):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self setupFetchedResultsController];
if (![[self.fetchedResultsController fetchedObjects] count] > 0 ) {
NSLog(#"!!!!! ~~> There's nothing in the database so defaults will be inserted");
[self importCoreDataDefaultRoles];
}
else {
UIViewController *mainViewController = (UIViewController *)self.window.rootViewController;
UINavigationController *readingsTVCnav = [[mainViewController viewController] objectAtIndex:0];
// Override point for customization after application launch.
return YES;
}
The fetching parts of the code are related to Core Data which already set up and working.
The reason for this change is that I want to have a plain view controller set up as the initial screen rather than the tabBarConfiguration.
Cheers Jeff
EDIT: I have added an image for clarity
From what you describe and depict in your image, you have the navigation controller set up as your app's root view controller. So you can access it (if you need to) in your app delegate's didFinishLaunchingWithOptions: method as follows:
UINavigationController *navController = (UINavigationController *)self.window.rootViewController;
Although for your app I don't think there's any reason you need to reference the nav controller from your app delegate. All the setup you need (with the exception of your code data code, which you say is already working) can be handled through the storyboard.
In your nagivationController's root viewController (the one with all the buttons) you should set up a segue from each button to the approperiate viewController in your storyboard. Bure sure to set then up as "push" segues so that it will push the view controller onto your navigationController's navigation stack. If there is any particular setup you need to do for the child view controller when the segue happens, you can implement the prepareForSegue: method like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ShowMyViewController"]) {
MyViewController *vc = (MyViewController*)segue.destinationViewController;
vc.title = #"My Title";
vc.someProperty = #"Some Value";
}
}
You can (and should) identify each of your segues in the storyboard with a unique identifier so that you can identify them when this method is called.

Resources