iOS - Navigational flow is missed - ios

In my application i am adding the child view controller using the following code.
self.onlineUserList = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"OnlineUserList"];
UINavigationController *navController=[[UINavigationController alloc]initWithRootViewController:self.onlineUserList];
self.onlineUserList.delegate = self;
navController.navigationBar.hidden = YES;
CGRect aRect = [[UIScreen mainScreen] bounds];
[navController.view setFrame:(CGRect){0, 0, aRect.size.width, aRect.size.height-47}];
[self addChildViewController:navController];
[self.view addSubview:navController.view];
[self didMoveToParentViewController:navController];
And i am removing the child view controller using following code.
[_onlineUserList removeFromParentViewController];
[_onlineUserList.view removeFromSuperview];
[_onlineUserList didMoveToParentViewController:nil];
It is working well. But after removing the child view controller then i am not able to do any action on the parent view controller. I think i am missing some thing navigational flow. Please help me.

Just get all Viewcontrollers in array , check this .
If you want all view controller of navigation ,
NSArray *currentControllers = self.navigationController.viewControllers;
if you want first pushedviewcontroller ,
UIViewController *firstcontroller = self.navigationController.viewControllers.firstObject;
if you want last object ,
UIViewController *Lastcontroller = self.navigationController.viewControllers.lastObject;
Now trace with this code , which viewcontroller you are removing .
or Another way is just give identifier of your parentviewcontroller in didfinishlaunchwithoption , like ,
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]];
SlideMenu = [storyboard instantiateViewControllerWithIdentifier:#"SlideMenu"];
then add method for topviewcontroller,
- (UIViewController *)topViewController{
return [self topViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}
- (UIViewController *)topViewController:(UIViewController *)rootViewController
{
if (rootViewController.presentedViewController == nil) {
return rootViewController;
}
if ([rootViewController.presentedViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController;
UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
return [self topViewController:lastViewController];
}
UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController;
return [self topViewController:presentedViewController];
}
now create one more method in your appdelegate to present your parentviewcontroller like ,
- (void)ShowMenu{
[SlideMenu ShowToViewController:[self topViewController]];
}
and then when you remove childviewcontroller then call ,
[[AppDelegate mainDelegate] ShowMenu];
I hope this info enough to solve your issue.

Related

Navigation bar not appearing when pushing a controller from a presented ViewController

I have following code to present a UIViewController (named A) with UINavigationController.
I have kept navigation bar hidden as I do not want it on A by using following code
[self.navigationController.navigationBar setHidden:YES];
but when I push UIViewController named B I want the navigation bar to be displayed but it's not working even If set
self.navigationController?.setNavigationBarHidden(false, animated: false)
on UIViewController B. Can someone help me in this?
Here is the code to present UIViewController A
AppDelegate* delegate = [AppDelegate applicationDelegate];
UIStoryboard *story = [UIStoryboard storyboardWithName:STORYBOARD_MAIN bundle:nil];
SelectionViewController *tutorial = [story instantiateViewControllerWithIdentifier:#"SelectionViewController"];
tutorial.providesPresentationContextTransitionStyle = YES;
tutorial.definesPresentationContext = YES;
[tutorial setModalPresentationStyle:UIModalPresentationOverCurrentContext];
tutorial.delegate = self;
UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:tutorial];
navController.modalPresentationStyle = UIModalPresentationFullScreen;
[navController.navigationBar setHidden:YES];
navController.providesPresentationContextTransitionStyle = YES;
navController.definesPresentationContext = YES;
[navController setModalPresentationStyle:UIModalPresentationOverCurrentContext];
[delegate.tabBarController presentViewController:navController animated:YES completion:NULL];
Here is the code to push UIViewController B
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"AppDetail" bundle:nil];
DetailViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"DetailViewController"];
[self.navigationController pushViewController:vc animated:YES];
[[self navigationController] setNavigationBarHidden:NO];
Additional information
UIViewController A is in different storyboard and B is in different
A's code is written in OBJ-C and B's code is in swift.

Push view controller not working

I'm using UINavigation and UIPageView controller in a UIViewController. I have 3 buttons and here is my code :
- (IBAction)StartButtonClicked:(id)sender {
NSLog(#"start button pressed");
[_Timer invalidate];
_Timer = nil;
dispatch_async(dispatch_get_main_queue(), ^{
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
CardViewController* controller = [storyboard instantiateViewControllerWithIdentifier:#"CardViewController"];
[self.navigationController pushViewController:controller animated:NO];
}
}
- (void)pageViewController {
self.pvc = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pvc.view.frame = self.view.bounds;
[self.view addSubview:self.pvc.view];
[self.pvc setViewControllers:#[[viewControllerArray1 objectAtIndex:0]] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
}
- (IBAction)rightButtonPressed:(id)sender {
[self pageViewController];
[self.pvc setViewControllers:#[[viewControllerArray1 objectAtIndex:1]] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
}
- (IBAction)leftButtonPressed:(id)sender {
[self pageViewController];
[self.pvc setViewControllers:#[[viewControllerArray1 objectAtIndex:2]] direction:UIPageViewControllerNavigationDirectionReverse animated:YES completion:nil];
}
When I click on right button and came back to this view controller my start button's push view not working.
Please check the answer .
iOS - Push viewController from code and storyboard
Don't initialize Storyboard always,
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
You can set the rootViewController from the UtoryBoard itself.
Set initialViewController as a UINavigationController and Set the FirstViewController as RootViewController.
Edit :
For Push use the below code,
CardViewController* controller = [storyboard instantiateViewControllerWithIdentifier:#"CardViewController"];
[self.navigationController pushViewController:controller animated:NO];
and Back. If use the default backButton not need to write any code.
If use are using custom backButton,
[self.navigationController popViewController];
Check 2 things,
Your storyboard identifier is correct.
The root view have navigation Controller.
Your StoryBoard should be like this,

How to present View Controller modally on app launch?

I've created a screen that ONLY appears the first time that my app is launched; it works great. However, I'd like to present this screen modally on top of my root view controller (EditorsNoteViewController being the screen I want to present modally on first launch). Does anyone know how I would do this? Here is what I have so far:
Appdelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedOnce"])
{
NSLog(#"not first launch");
self.viewController = [[UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:#"articlesNav"];
self.window.rootViewController = self.viewController;
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedOnce"];
[[NSUserDefaults standardUserDefaults] synchronize];
self.viewController = [[UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:#"articlesNav"];
self.window.rootViewController = self.viewController;
EditorsNoteViewController *vc = [[EditorsNoteViewController alloc]init];
[self.viewController.navigationController presentViewController:vc animated:YES completion:nil];
}
[self.window makeKeyAndVisible];
return YES;
}
I figured out that you'll have to call
[self.window makeKeyAndVisible];
before presenting the view controller
I have read about and encountered this issue many times. A way to completely get around this issue (without any flickering or warnings) is to consider a custom container view controller that will contain your root view controller and your modally presented view controller, as child view controllers. The container view controller can then transition between the two with custom animations, with a very similar effect as present/dismiss.
Let's consider the following container view controller :
#implementation ContainerViewController {
NSMutableArray* stack;
}
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
stack = [NSMutableArray new];
}
return self;
}
- (void) pushViewController:(UIViewController*) viewController {
UIViewController* currentViewController = [stack lastObject];
[stack addObject:viewController];
if (self.isViewLoaded) {
[self addChildViewController:viewController];
[currentViewController willMoveToParentViewController:nil];
viewController.view.frame = CGRectOffset(self.view.bounds, 0, self.view.bounds.size.height);
[self transitionFromViewController:currentViewController toViewController:viewController duration:0.3 options:kNilOptions animations:^{
viewController.view.frame = self.view.bounds;
} completion:^(BOOL finished) {
[viewController didMoveToParentViewController:self];
[currentViewController removeFromParentViewController];
}];
}
}
- (void) popViewController {
UIViewController* currentViewController = [stack lastObject];
[stack removeLastObject];
if (self.isViewLoaded) {
UIViewController* viewController = [stack lastObject];
[self addChildViewController:viewController];
[currentViewController willMoveToParentViewController:nil];
[self transitionFromViewController:currentViewController toViewController:viewController duration:0.3 options:kNilOptions animations:^{
[self.view sendSubviewToBack:viewController.view];
currentViewController.view.frame = CGRectOffset(self.view.bounds, 0, self.view.bounds.size.height);
} completion:^(BOOL finished) {
[viewController didMoveToParentViewController:self];
[currentViewController removeFromParentViewController];
}];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
UIViewController* viewController = [stack lastObject];
[self addChildViewController:viewController];
[self.view addSubview:viewController.view];
[viewController didMoveToParentViewController:self];
}
#end
In your AppDelegate, you can then have something along those lines :
ContainerViewController* viewController = [ContainerViewController new];
[viewController pushViewController: [RootViewController new];
if ([self shouldPresentModalViewController]) {
[viewController pushViewController: [ModalViewController new];
}
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
Somewhere in the modal view controller, you can use the following code to dismiss it:
[((ContainerViewController*) self.parentViewController) popViewController];
Assuming that you have created your EditorsNoteViewController in a storyboard, you should initialise it like this:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
EditorsNoteViewController *vc = (EditorsNoteViewController*)[storyboard instantiateViewControllerWithIdentifier:#"EditorsNoteOrWhateverIdentifierYouChose"];
Try to check your NSUserDefaults in your RootViewController -viewDidLoad, and if it is the first time, present the new UIViewController there (So that you don't need to mess around with your AppDelegate).
If you want to present it animated, you can do it in -viewDidAppear instead of -viewDidLoad.

Login Screen and Navigation controller

So in my App.Delegate I'm doing this -
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.loginViewController = [[LoginViewController alloc] initWithNibName:#"LoginView" bundle:[NSBundle mainBundle]];
UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController: self.loginViewController];
self.window.rootViewController = navigation;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
and in my login controller I'm doing this -
- (IBAction)login:(UIButton *)sender {
NSString *username = self.userName.text;
NSString *password = self.password.text;
[AccountUtils emailLogin:username password:password useCookie:true callback:^(NSDictionary *loginResponseJSON){
if([loginResponseJSON count] != 0){
[self performSelectorOnMainThread:#selector(displaySearchController) withObject:nil waitUntilDone:YES];
// [self performSelectorOnMainThread:#selector(switchState) withObject:nil waitUntilDone:YES];
} else {
//incorrect entry info view here.
}
}];
}
- (void) displaySearchController {
SearchViewController *searchViewController = [[SearchViewController alloc] initWithNibName:#"SearchView" bundle:[NSBundle mainBundle]];
UINavigationController *navigator = self.navigationController;
[navigator popViewControllerAnimated: YES];
[navigator pushViewController: searchViewController animated:YES];
}
If I correctly login, I go to the second controller's view, but at the top I'm still allowed to go 'back' to the login page. I don't want that to happen and I thought this case would be taken care off by the popViewControllerAnimated line. How do I make it so that when I login, I am not allowed to go back to the login page?(in other words, I guess pop the login controller off the navigation controller's stack?)
If you want just to remove the loginVC you could set the new navigationController as the rootViewController of the AppDelegate after the user has logged in. So you could move the displaySearchController method in the AppDelegate and call this method (from the loginVC) after the user has logged in:
-(void)displaySearchController{
SearchViewController *searchViewController = [[SearchViewController alloc] initWithNibName:#"SearchView" bundle:[NSBundle mainBundle]];
UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController: searchViewController];
self.window.rootViewController=navigation;
}
Otherwise, if you really want the push animation, you can remove the loginVC from the navigationController viewControllers stack in the viewDidAppear of SearchDispalyController:
-(void)viewDidAppear:(BOOL)animated{
NSMutableArray *stackVCs=[self.navigationController.viewControllers mutableCopy];
int idx=[stackVCs indexOfObject:self];
//this remove the previous viewcontroller from the stack
[stackVCs removeObjectAtIndex:idx-1];
self.navigationController.viewControllers=stackVCs;
[super viewDidAppear:animated];
}
Also in the loginVC just before push the searchVC you should call this to hide the back button:
[navigation.navigationItem setHidesBackButton:YES];
You don't want to use a UINavigationController with the LoginViewController and you don't want to push the SearchViewController. Instead, use a UINavigationController with SearchViewController and when you display it, make it the rootViewController.
Do this to achieve what you want.
Move the displaySearchController method to your AppDelegate.m file
Do declare the displaySearchController method in AppDelegate.h file
Now Define the displaySearchController method in AppDelegate.m file as :
- (void) displaySearchController {
SearchViewController *searchViewController = [[SearchViewController alloc] initWithNibName:#"SearchView" bundle:[NSBundle mainBundle]];
UINavigationController *navigator = [[UINavigationController alloc]initWithRootViewController:searchViewController];
self.window.rootViewController = navigator;
}
Call a new local method showNewViewController from your loginController as :
[self performSelectorOnMainThread:#selector(showNewViewController) withObject:nil waitUntilDone:YES];
Now define showNewViewController in your loginController.m file as
-(void)showNewViewController {
AppDelegate *appDele = [UIApplication sharedApplication].delegate;
[appDele displaySearchController];
}
Don't forget to import the AppDelegate.h file to your loginController.m
This will certainly help you.

Switching back and forth in views

very new to obj-c.
I´ve made a simple MainViewController, loading its view from a xib. (From a standard single view template)
On the main view is a button that takes me to another view. Below is the Method:
-(IBAction)forward:(id)sender
{
tvc = [[TrackViewController alloc] initWithNibName:nil bundle:[NSBundle mainBundle]];
[self.view addSubview:tvc.view];
}
On the other view (also loaded from xib) i have a button to take me back to the main view.
Here is my IBAction for returning to the main view:
-(IBAction) back:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
However it does not return to the main view. Not with popToRootViewControllerAnimated: either.
Any response will be appreciated!
This answer applies when you are not using UINavigationBar
In your Forward button you need to write.
TrackViewController *lf = [[TrackViewController alloc]initWithNibName:#"TrackViewController" bundle:nil];
[self presentViewController:TVC animated:YES completion:nil];
and in the Backward screen you should right.
[self dismissViewControllerAnimated:NO completion:nil];
You should load your MainViewController in your AppDelegate in an UINavigationController
MainViewController *mainVC = ...
UINavigationController *naviVC = [[UINavigationController alloc] initWithRootViewController:mainVC];
self.window.rootViewController = naviVC;
Then you can go forward in your MainViewController:
-(IBAction)forward:(id)sender;
{
tvc = [[TrackViewController alloc] initWithNibName:nil bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:tvc animated:YES];
}
and go back in your other ViewController:
-(IBAction) back:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
If your MainViewController is inside a UINavigationController, then the forward method should look like this:
-(IBAction)forward:(id)sender
{
tvc = [[TrackViewController alloc] initWithNibName:nil bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:tvc animated:TRUE];
}

Resources