I have an iOS 7 app with a side hamburger menu and a main table view controller where I display content. Whenever the user selects an item in my side menu, I'm hiding the side menu and I want to reload data in the main view controller. My initial thought was to put my data refreshing code in my main view controller's viewWillAppear:
But when I set a breakpoint in viewWillAppear:, I get 2 calls when the view controller initially appears, one from UIViewController itself, and another from [ECSlidingViewController viewWillAppear:] where the following line seems to call my viewWillAppear: again
[self.topViewController beginAppearanceTransition:YES animated:animated];
On the other hand, when I show the left menu and then hide it, my view controller's viewWillAppear: is not called this time, so data is not refreshed in my case.
Did I miss something in my configuration somewhere? Is that a bug or a feature? How should I use it?
PS: I used to use IIViewDeckController and I had the exact same problem, so I switched to ECSlidingViewController because it said that "Your view controllers will receive the appropriate view life cycle and rotation methods at the right time.".
As a matter of fact, I managed to do what I wanted with another library: https://github.com/romaonthego/RESideMenu
I had to implement delegate methods in order to call lifecycle methods on my view controller when menu is shown or hidden:
- (void)sideMenu:(RESideMenu *)sideMenu willShowMenuViewController:(UIViewController *)menuViewController {
[sideMenu.contentViewController viewWillDisappear:YES];
}
- (void)sideMenu:(RESideMenu *)sideMenu didShowMenuViewController:(UIViewController *)menuViewController {
[sideMenu.contentViewController viewDidDisappear:YES];
}
- (void)sideMenu:(RESideMenu *)sideMenu willHideMenuViewController:(UIViewController *)menuViewController {
[sideMenu.contentViewController viewWillAppear:YES];
}
- (void)sideMenu:(RESideMenu *)sideMenu didHideMenuViewController:(UIViewController *)menuViewController {
[sideMenu.contentViewController viewDidAppear:YES];
}
And viewWillAppear is not called twice initially.
Related
I'm implementing my own 'back' button. Where onClick, the following code is executed in the ViewController (VC) being dismissed:
Dismiss current VC (VC#1)
Pop current VC (VC#1) off my custom navigationStack
Get the last VC (VC#2) from the navigationStack, and present it using
presentViewController
What happens is the back works visually works - i.e. current VC disappears, previous VC appears. However, the viewDidLoad method is not called. So the screen isn't updated with data updates from viewDidLoad.
[self dismissCurrentViewController:self completion:^{
[TWStatus dismiss];
FHBaseViewController *vcToDisplay = [[FHDataManager sharedInstance] popNavigationStack];
[vcToDisplay.homeVC presentViewController:vcToDisplay animated:NO completion: ^{ }];
}];
Questions:
I was under the impression that viewDidLoad always gets called when presentViuewController is used??
I 'build' the screen using a method called ONLY from viewDidLoad in VC#2. How is iOS displaying the screen without coming into viewDidLoad?
btw, I'm not using storyboards. Any help is appreciated!
My guess is that viewWillAppear is being called but viewDidLoad is not, at least not when you expect it is. viewDidLoad should be called once, but depending on how you're managing the view controllers, viewDidLoad may not be triggered every time your view appears (which happens after loading).
The completion handler is called after the viewDidAppear: method is called on the presented view controller. from presentViewController doc
so put this in your code with a breakpoint on the call to super and verify it is getting called when this transition occurs.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
edit: since you verified that viewWillAppear is getting called, then I would say that it's coming down to how you are managing the view controller life cycle. Even with a standard UINavigationController, viewDidLoad is not called when a view is shown as a result of popping items on the navigation stack. I would move your logic to viewWillAppear if you are dead set on not using UINavigationController
When I make a back button pragmatically I use:
[self.navigationController popViewControllerAnimated:YES];
This will invoke the viewDidLoad method. Use that instead of your current code.
Is there a way to tell if a new controller came from a navigation back button or was pushed onto the stack? Id like to reload data only for pushing on the navigation stack, not on a back button press.
As of iOS 5.0 you can do this:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (self.isBeingPresented || self.isMovingToParentViewController) {
// "self" is being shown for the 1st time, not because of a "back" button.
}
}
If your push also includes instantiating the view controller, put your push-only logic in viewDidLoad. It will not be called on back because it has already been loaded.
You could implement the UINavigationControllerDelegate and override the `navigationController:didShowViewController:animated:' method. You'll then have to check the returned view controller to make a determination as to whether you came back from the expected view controller.
- (void)navigationController:(UINavigationController*)navigationController didShowViewController:(UIViewController*)viewController animated:(BOOL)animated
{
if (yourPushedViewController == viewController)
{
// Do something
}
}
I have a view controller in my application where on my screen I have a UIView that the user is required to tap on. When they do that, I want to call another viewController's view, and display it on the screen for the user. Unfortunately, I am having trouble displaying the view.
The name of my viewController that I am making the call from is called "MainViewController", and the ViewController whose view I wish to display is called, "NextViewController"
Here is my code from where I make the call:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"I was touched.");
_nextView = [[NextViewController alloc] init]; //this code is not being called
[self.view addSubview:_nextView.view]; //neither is this being called
}
Where _nextView is a property that I declare in the .h file of MainViewController.
This method is being called, but for some reason because I am able to see the log statements print to the output, but for some reason I am unable to call the lines after that. What am I doing wrong?
You shouldn't add the view of another view controller to your view without making that view controller a child view controller. If you just want a view, then set one up in a xib file and add it to your view as a subview. If you want to use a view controller, then you should present it modally, and dismiss it when you're done. This kind of situation where you want to gather some info from the user to use in your app, is an appropriate place to use a modal view controller. MainViewController should set itself as the delegate of NextViewController, and NextViewController should define a delegate protocol to send the data back to MainViewController.
To present it modally, do this:
_nextView = [[NextViewController alloc] initWithNibName:#"your nib name here" bundle:nil];
[self presentViewController:_nextView animated:YES completion:nil];
Are you using a Navigation Controller? Or Storyboards? One way of displaying another view controller would be like this:
[self presentViewController:_nextView animated:YES completion:^{
}];
A couple of things:
- If your NSLog gets called, then so do the other two lines you say do not.
- I assume you mean you want to display the other view controller on screen, not display the other view controller's view on the first view controller. These are two very different things, the second of which you wouldn't want to do.
NOTE:
Before reading this question please note that I have read the previous questions that explain the deficiencies regarding apple's implementation of UISplitViewController and how I should use the open-sourced "MGSplitViewController" because its not too easy to simply hide the master view controller on a split view controller in landscape-mode. Please keep in my mind that I'm limited to using the normal UISplitViewController in iOS 5.1.
Now onto the question:
I have a split view controller with table views on the left side (master view) and a detail view controller on the right. I'm using a navigation controller to control the left side which is a table view that transitions onto another table view ("DataTableViewController"). In order to hide this left side, I have placed a "hide" button on the navigation tool bar of the detail view controller. When the hide button is pressed, I change my "_hideMaster" property:
-(IBAction)hidePressed
{
_hideMaster = !_hideMaster;
// Must manually reset the delegate back to self in order to force call "shouldHideViewController"
self.splitViewController.delegate = nil;
self.spliteViewController.delegate = self;
}
and then automatically this method is called in the SplitViewController delegate:
// This is called when I change the delegate from nil back to self.
- (BOOL)splitViewController: (UISplitViewController*)svc shouldHideViewController: (UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation
{
return _hideMaster;
}
When I debug it, I can see that everything goes according to plan and the property has the correct value when it enters the method splitViewController:shouldHideViewController:inOrientation:
The only problem is that nothing happens. My left most table view (DataTableViewController) does not disappear. When I look closer, the (UIViewController *)vc parameter in the delegate method is not the table view controller that I want to hide but instead the navigation controller associated with this table view. So essentially it is trying to hide the navigation controller - which is clearly not what I want...
How can I make it so that the UIViewController parameter in the automatically called delegate method (shouldHideViewController:) calls the topmost view controller associated with that navigation controller? (After all, I want to hide DataTableViewController)
Here's how I handle it. Might need more work for making the MasterViewController reappear if it is not instantiated on the way back.
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.30f];
[[self.splitViewController.viewControllers lastObject] view].frame = self.splitViewController.view.frame;
[UIView commitAnimations];
I have a popover that has a main view that is used to pick from several sub-views and I'd like to reset the popover to the main view when the user taps outside of the popover and closes the window.
For example, the main popover view has Options, Categories, WordList, and Results. I'd like to make it so that the next time the user invokes the popover, they go to the main view rather than back to the view they were on when they last closed the popover.
If I'm only one level deep, I can use
- (void)viewWillDisappear:(BOOL)animated {
[[self navigationController] popToRootViewControllerAnimated:YES];
[super viewWillDisappear:animated];
}
But if I'm deep in a hierarchy, e.g. WordList:Category:Words I can't pop to root in WordList or Category since when the view disappears, I want to go to the next level down. If I use the code listed above, I can't get down a level. I pop to the root view.
What I'd like is to be able to tell the popover view to pop to its root when it's dismissed. Something like this:
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
[[self popover] popToRootViewControllerAnimated:YES];
[self.popoverController dismissPopoverAnimated:YES];
}
Any thoughts?
I believe that what you want is UIPopoverController's contentViewController property, which will be whatever view controller you initialized the popover with--most likely that navigation controller. This...
[self.popover.contentViewController popToRootViewControllerAnimated:YES];
...should do the trick for you.
When initializing the popover, I test to see if it already exists. If so, I skip the initialization and go directly to the popover. That's why, when the popover is dismissed, it stays in whatever view the user left it in.
To make it start at the first view, I just need to set the popover to nil.
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popOverController {
[self.popoverController dismissPopoverAnimated:YES];
self.popoverController = nil;
Now when the user invokes the popover, it creates a new set of views, starting with the main menu.