NB: First App;
I am currently trying to create a log out button for an app that I have created. Essentially, on load, the user is presented with LoginViewController.xib with 2 text fields and a button, given that the text fields meet the arguments, when the button is pushed, the following argument is executed:
if (success) {
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate.window setRootViewController:appDelegate.tabBarController];
}
This works fine, and the user is taken into the app with a tab controller that switches between 3 xib's (Home, Settings, Table).
In the settings tab, I have a button "LogOut" which when pressed, I would like the user to be returned to the "LoginViewController.xib" but I can't seem to find any way of doing this from tutorials on youtube or on the web.
Please see below for the Settings coding;
SettingsViewController.h:
#import <UIKit/UIKit.h>
#interface SettingsViewController : UIViewController
- (IBAction)LogOutClick:(id)sender;
#end
SettingsViewController.m:
#import "SettingsViewController.h"
#interface SettingsViewController ()
#end
#implementation SettingsViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)LogOutClick:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
Calling dismissViewControllerAnimated: is not going to help as your tab bar controller/settings view controller is not presented by any view controllers. The original login controller, as far as I can tell from the code you have supplied, no longer exists in memory and, as a result, you can not "return" to it.
You have two options: one is to present the login view controller on the tab bar controller, and the other is to change the window's root view controller to the login controller. For example
//Present over tab bar
- (IBAction)LogOutClick:(id)sender {
LoginViewController *loginController = [[LoginViewController alloc] initWithNibName:#"LoginViewController" bundle:nil];
[self presentViewController:loginController animated:YES completion:nil];
}
//Switch root view controller
- (IBAction)LogOutClick:(id)sender {
LoginViewController *loginController = [[LoginViewController alloc] initWithNibName:#"LoginViewController" bundle:nil];
[appDelegate.window setRootViewController:loginController];
}
In my opinion, the first method is the better way of doing things. After the login controller has been dismissed, you can then populate the tab bar controller's views with the proper data. However, as the login controller already has a method that switches the root view controller of the window, it may be easier for you to just switch the root view controller back to the login controller.
Related
I knew its a duplicate. But still having an issue and even when tried with possibilities didn't work. Hence posting the same to reach a solution. Hope to get help from you guys.
The initial is embedded inside UINavigationController. For the initial (the landing view) the navigation bar must be hidden. The other views when called from the landing view - must show the navigation bar.
I'm handling the hide & show of navbar in the landing view by overriding the methods of the view as follows:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Hiding the navigationbar hidden for the first page
[[self navigationController] setNavigationBarHidden:YES animated:YES];
}
// Even tried animated:NO & animated:animated
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
// Showing the navigationbar hidden for the first page
[[self navigationController] setNavigationBarHidden:NO animated:YES];
}
While the app loads initially, the nav bar is in hidden state (as expected & working fine). When coming back to the landing view from the child view controller, the nav bar gets hidden after some seconds - the landing view gets loaded on to the ui screen.
I also tried using the navigationcontroller delegate method in landing view: navigationController: willShowViewController: animated:. But unable to reach the solution that i need.
Hence i provided the navigationcontroller delegate in one of my childviewcontroller and checked whether the childcontroller when popped is not in viewcontrollers of the navigationcontroller using if condition. When yes, then i provided the hide option of the navigationbar. but also failed to have the solution.
During surfed, there was a solution to handle with viewanimation. I tried and that too failed.
Again surfed, the solution provided across is to handle the similar issue with viewwillappear & viewwilldisappear. I'm blinked since the way i'm doing is similar to the proposed way. Even then unable to reach a solution.
FYI.. I'm using Xcode 6.3 and deployment target is 6.0 onwards. I'm using storyboard to manage views.
Please help me sort the issue... App loads is hiding the nav bar in landing page. But when landing page is loaded back from a child view then the nav bar gets hidden only after the landing page loaded on to the ui. I do need to get hidden of the nav bar as like when app loads, when the child view pops and the landing view gets loaded on the top of the controller.
If you want to hide navigation bar in the second view then don't try to manage in viewWillAppear and viewWillDisappear because I have faced a lot of problems by trying like that and it also effected the constraints. Just use delegate for navigation controller in appDelegate it is working fine for me.
self.navigationController.delegate = self;
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
if ([viewController isKindOfClass:[LoginViewController class]])
{
[self.navigationController setNavigationBarHidden:YES animated:animated];
} else {
[self.navigationController setNavigationBarHidden:NO animated:animated];
}
}
Use this method:
[navigationController setNavigationBarHidden:YES];
So, if you are in some view controller:
[self.navigationController setNavigationBarHidden:YES];
More clarifications:
UINavigationController has a property navigationBarHidden, that allows you to hide/show navigation bar for whole nav controller.
Let's look at the next hierarchy:
--UINavigationController
----UIViewController1
----UIViewController2
----UIViewController3
Each of three UIViewController will have nav bar since they are in UINavigationController. For example, you want to hide bar into the second (actually it doesn't matter in which one), then write into UIViewController2:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES]; //it hides
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.navigationController setNavigationBarHidden:NO]; // it shows
}
Overwrite the delegate method in your custom UINavigationController class:
-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[self setNavigationBarHidden:NO];
if ([viewController isKindOfClass:[SomeViewController class]])
{
[self setNavigationBarHidden:YES];
}
}
One advantage for putting it in your UINavigationController class is that you don't clutter your UIViewController class with code
Tested and works.
UPDATE
Create a UINavigationController subclass: f.e. MyNavigationController
In AppDelegate.h:
#import "MyNavigationController.h"
#property (nonatomic) MyNavigationController *navigationController;
Then initialise it in AppDelegate.m:
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//Probably some more code here
self.navigationController = [[MyNavigationController alloc] initWithRootViewController:yourRootViewController];
self.window.rootViewController = self.navigationController;
self.window.backgroundColor = [UIColor blackColor];
[self.window makeKeyAndVisible];
return YES;
}
Then overwrite the delegate method in your custom UINavigationController class
I have little to no experience with storyboards so not really sure how to setup a custom UINavigationController, but this is how I do it in code.
Here's another SO post how to create a custom UINavigationController to use with storyboards.
I have a UITabBarController-based application in which my login page is presented modally by the default tab's VC(UITabBarController index 0), and is dismissed modally by dismissViewControllerAnimated:.
From my settings page I have a logout button, and if the user immediately logs back in I have to call [self.presentingViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil]; and then reset the UITabBarController's selectedIndex property in order to get rid of the settings page and also go back to the starting tabBar tab. So I do this:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString* responseString = [[NSString alloc] initWithData:_responseData encoding:NSUTF8StringEncoding];
/*Succesful Login*/
if([responseString isEqualToString:#"success"])
{
UITabBarController *tabBarController = (UITabBarController *)self.presentingViewController;
if (tabBarController){
NSLog(#"I have a tab bar~");
[self.tabBarController setSelectedIndex:0];
}
else{
NSLog(#"I don't have a tab bar~");
}
//dismisses from a second, immediate, re-login attempt
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil];
//dismisses from first login attempt
[self dismissViewControllerAnimated:YES completion:nil];
NSLog(#"Succesful login.");
}
In this case, the method setSeletedIndex: does not work because this methodology essentially creates a new instance of the tabBarController, when it is the original that is actually displayed. I CAN NOT seem to access the already existing UITabBarController which was before my modally presented login page.
EDIT:
I have tried using this conditional to check its existence:
if (self.tabBarController){
NSLog(#"I have a tab bar~");
//[self.tabBarController setSelectedIndex:0];
}
else{
NSLog(#"I don't have a tab bar~");
}
where I have even changed self.tabBarController to self.presentingViewController.tabBarController and get nil, and self.presentingViewController.presentingViewController.tabBarController and get nil, how am I to access the previously existing UITabBarController?
Objective: Access the application's UITabBarController that is present before the presentation of the login page. (Access the Appdelegate's UITabBarController)
Your use case is a natural fit for delegation.
In the header file for your Login, define a delegate. E.g.,
#class Login
#protocol LoginDelegate <NSObject>
#required
- (void) userDoneWithLoginController:(Login *) controller;
#end
#interface Login: UIViewController
#property (weak, nonatomic) id<LoginDelegate> viewDelegate;
#end
Then your presenting view controller segue can set itself as the delegate to the login view controller by setting this "viewDelegate" object inside the prepareForSegue for the presenting view controller before control passes to the Login page. [Let me know if you want this clarified.]
In addition the presenting view controller will implement the required method--which can simply dismiss the login page when called
-(void) userDoneWithLoginController:(Login *) controller
{
[controller dismissViewControllerAnimated:YES completion:nil];
}
Once you've done this, the login page simply calls its delegate method when it is done, thusly:
[self.viewDelegate userDoneWithLoginController:self];
Viola!
This is preferred way to dismiss a modal view in iOS. The calling controller should be the controller dismissing the view. Try to avoid having view controllers dismiss themselves. Instead, have their calling/presenting controllers (delegates) dismiss them.
The following should work.
// check that the vc presenting your current modal vc is a tab bar controller
if ([self.presentingViewController isKindOfClass:[UITabBarController class]]) {
// get the pointer of type UITabBarController
UITabBarController *tabBarController = (UITabBarController *)self.presentingViewController;
// set to the desired tab
tabBarController.selectedIndex = 2;
}
// dismiss the current modal vc
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
I believe I found my solution.
I was able to access the app's UITabBarController from the Appdelegate like so:
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[[appDelegate myTabBar] setSelectedIndex:0];
I'm new to iOS programming and I'm facing a problem
I'm having a problem with custom delegate.
I'm trying to make a simple custom where it return data to the previous view controller and pop the current view controller.
I have 2 navigation view controller
1 - main view controller
2 - Adding
and here is the protocol that is written in the adding view controller
#protocol AddingDelegate <NSObject>
#required
-(void)setInformation:(Adding *)controller withObject:(Conference *)info;
and here is the where I called it in adding view controller
-(IBAction)addingConference
{
NSLog(#"Adding Button Pressed");
conferenceObject = [[Conference alloc]init];
conferenceObject.name = [NameTX text];
conferenceObject.city = [CityTX text];
conferenceObject.description = [Dectription text];
NSMutableArray *info = [[NSMutableArray alloc] init];
[info addObject:conferenceObject];
[self.delegate setInformation:self withObject:conferenceObject];
NSLog(#"adding Conference method is done");
}
I wrote the delegate at the interface in the main view controller
#interface MainViewController : UITableViewController <AddingDelegate>
#end
and here where I declared the delegate method
-(void)setInformation:(Adding *)controller withArray:(NSMutableArray *)info
{
NSLog(#"in the main view at the delegate");
[self.navigationController popToRootViewControllerAnimated:YES];
NSLog(#"Should be popped right now");
}
and this is the prepare for segue method
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"AddObject"]) {
UINavigationController *navigation = segue.destinationViewController;
Adding *addingViewController = [[navigation viewControllers]objectAtIndex:0];
addingViewController.delegate = self;
}
}
now the problem is when I push the adding on top of the stack and then fill the information and press done the adding view controller doesn't pop to show main view controller.
I tried to log everything and the logs from the main view controller doesn't show .
Please help me
What I notice here is that in the implementation of prepareForSegue:sender: the segue's destinationViewController is a navigation controller. This makes me think that your segue is not pushing the AddingController on the current navigation stack but it's presenting a new one instead. This means the new navigation controller containing the AddingController is presented modally and as such, when you try to pop the navigation stack nothing seems to happen because you're operating on the wrong navigation stack. If that is the case you have two options: 1. change [self.navigationController popToRootViewControllerAnimated:YES]; for [self dismissViewControllerAnimated:YES completion:nil]; or 2. change the segue to be a push segue instead of a modal segue and point the segue directly to the AddingController.
In Adding.m
#class Adding;
#protocol AddingDelegate
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;
#end
#interface Adding : UIViewController
#property (weak, nonatomic) id <AddingDelegate> delegate; // have you forgot this one
#end
and use
[self dismissViewControllerAnimated:YES completion:nil];
You need dismiss if you want get back to previous screen and make sure you have added Navigation controller
First, have a look at this screenshot of my storyboard:
It is an application for a sound map. The user can either record a new field recording or chose an existing one from the library and upload them. The ViewController where the user has to add a title/description etc. (or modifies them when coming from the library) is presented modally (on the top right).
If the user choses to cancel this and to delete the recording, he shall return to the recording screen, if he comes from there, otherwiese to the library. If he choses to save/upload the recording, he shall return to the library, where the upload progress will be displayed.
How can I come back to the desired ViewController independently of the ParentViewController that I come from?
I thought about unwindSegue, but that doesn't work for my layout. Then I figured out a dirty workaround where I changed the selected tab of the TabBarController. But then I also want to set up the delegate correctly to pass some data.
Any thoughts are welcome!
I don't think changing the selectedIndex of the tab bar controller is a "dirty workaround" -- that's the way to do it, given your set up. It appears that you would want the EntryViewController (Library) to be the delegate of the EntryDetailViewController. If that's so, you could set up the delegate in the viewDidLoad method of the EntryDetailViewcontroller like this:
#import "DetailViewController.h"
#import "TableController.h"
#interface DetailViewController ()
#property (strong,nonatomic) UITabBarController *tbc;
#end
#implementation DetailViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tbc = (UITabBarController *)self.presentingViewController;
self.delegate = (TableController *)[(UINavigationController *)self.tbc.viewControllers[1] topViewController];
}
- (IBAction)saveAndUpload:(UIButton *)sender {
[self.tbc setSelectedIndex:1];
[self.delegate saveRecording:#"test recording"];
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)delete:(UIButton *)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
In my example, I just have two buttons to either save or cancel, and my TableController would be the same as your Library controller.
I'm pretty new to iOS dev, and I've been reading a ton on this problem, but still can't figure it out.
I have a button in an action sheet that is supposed to activate and present a slide-up modal that is a from/to date picking screen (it has its own controller DatePickerViewController. The action sheet is triggered by a button in the toolbar of a NavigationViewController's subview ("Map of Shows" view, top left button). Graphic shows the current storyboard relationships:
The code for this sequence looks like this:
// ShowsContainerController.m
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if( buttonIndex == 1 ){
// 1. Activates from actionsheet
[(NavigationViewController *)self.parentViewController showDateSelect];
}
}
// NavigationViewController.m
// 2. fires up the datepicker view
-(void)showDateSelect
{
pickerView = [[DatePickerViewController alloc] init ];
[self presentViewController:pickerView animated:YES completion:nil];
}
// DatePickerViewController.m
// 3. Instantiation of this controller. Definitely fires nslog.
-(void)viewDidLoad
{
NSLog(#"Here");
}
Once "here", the screen just goes black. I assume this is because I'm not doing something right either with the instantiation of the date picker controller, or with the segue to it. All of the views in question are associated with their respective controllers in the storyboard configs. To further confuse the issue, I have a UITableViewController that I created for another screen, and just for shits and giggles, attempted to load that, and it worked fine. I then created another completely separate UIViewController, pointed it back to the controller files that control the current non-working one, and it bombs as well, so I'm thinking the issue are the header and main files for the non-working UIViewController. ed. scratch that last note; I created a completely new header, main file and NIB for the view and it still didn't work. I don't know what the hell the deal is.
Any and all help would be greatly appreciated.
Addendum
- (IBAction)showOptions:(id)sender
{
NSString *showTypes;
if( self.onlyShowPreferredEvents ){
showTypes = #"\u2713 Only shows that I'll like";
} else {
showTypes = #"Only shows that I'll like";
}
_showDisplayOptionsActionSheet = [[UIActionSheet alloc] initWithTitle:#"Event Display Settings" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles: showTypes, #"Date Range", nil];
[_showDisplayOptionsActionSheet showFromTabBar:self.tabBarController.tabBar];
}
Per the comments.
Addendum 2
// All of DatePickerViewController.m
#import "DateRangeViewController.h"
#interface DateRangeViewController ()
#end
#implementation DateRangeViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
// All of DatePickerViewController.h
#import <UIKit/UIKit.h>
#interface DateRangeViewController : UIViewController
#end
I would disconnect the DatePickerViewController from the Nav controller and leave it as a stand alone VC.
Then get rid of showDateSelect and change clickedButtonAtIndex method to the following:
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if( buttonIndex == 1 )
{
pickerView = [[DatePickerViewController alloc] init ];
[self.navigationController pushViewController:pickerView animated:YES];
}
}
Here you are pushing the pickerView unto the Navigation controller stack. You will need to pop it to remove it.
you can also experiment with present/dismiss but I am not sure whether it would work on Nav Controller stack.
Update:
I am not sure why you are re-declaring the viewDidLoad in your DatePickerViewController.h file. Take that out.
Also in all view Did/Will Load/Appear methods, you need to start by calling super. So insert [super viewDidLoad] as a first line in your viewDidLoad method of DatePickerViewController.m
Figured it out. The problem is that when you're using a storyboard, like I am, and you're programmatically presenting a view, you need to begin by creating a storyboard object:
// "MainStoryboard_iPhone" is your .storyboard file's name
// [NSBundle mainBundle] returns the main storyboard bundle.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone"
bundle:[NSBundle mainBundle]];
Next, you instantiate the view that you want to present using that storyboard object:
// Give your view an identifier in the storyboard. That's how the storyboard object will find it.
// You should see it in the right panel options when you click on the view.
UIViewController *dateRangeController = [storyboard instantiateViewControllerWithIdentifier:#"dateRange"];
Present.
[self presentViewController:dateRangeController animated:YES completion:nil];
My mistake was in thinking that if you simply associated the view with the viewcontroller file (under the Class property in the config pane), that's all that was needed. But if you're using a storyboard, the UI process is expecting views to be instantiated through it, whether automagically or programmatically (presumably because more than one view can use the same view controller).