It is easy.
Suppose I have two views: firstView and SecondView.
firstView is the ROOT view.
I load secondView from firstView:
secondView *secondViewController;
secondViewController = [[SecondView alloc]
initWithNibName:#"SecondView" bundle:nil];
[self.view addSubview:SecondViewController.view];
I added a "Back" button in secondView.
When I click that button I go back to firstView:
[self.view removeFromSuperView];
Here is the question:
When firstView -> secondView, viewDidLoad in secondView is triggered.
How can I trigger an event to inform firstView when I go back using removeFromSuperView in secondView?
What do you want to do exactly ? maybe you just want to go from your first view controller to your second view controller ? and then go back to your first view controller ? in this case just do like this, if your first controller is already embbeded in a navigation controller:
[self.navigationController pushViewController:secondViewController animated:YES];
But if you just want to create a view from Interface Builder and then add it above your first view, you can use the notification center to post an event when you click on your Back button from your second view:
- (void) backButtonClicked:(id)sender {
[self.view removeFromSuperView];
[[NSNotificationCenter defaultCenter] postNotificationName:#"back"
object:self.view];
}
You can then add an observer for this event in your first view controller, like this:
- (void) pushSecondViewController {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(secondViewDidGoBack:)
name:#"back"
object:secondViewController.view];
SecondView *secondViewController = [[SecondView alloc] initWithNibName:#"SecondView"
bundle:nil];
[self.view addSubview:secondViewController.view];
}
- (void) secondViewDidGoBack:(NSNotification *)notification {
[[NSNotificationCenter defaultCenter] removeObserver:self];
NSLog(#"My Second View Did Go back !");
}
I hope this will help you !
Related
In sesssion inactivity implementation for my project. I have created a NSNotification in RootViewController class of project.
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle: #"Close"
style: UIBarButtonItemStyleDone
target: self
action: #selector(closeModal)];
UIImage *image = [UIImage imageNamed:#"fidelity_logotype"];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
[imageView setImage:image];
[self.navigationItem setTitleView:imageView];
self.navigationController.view.backgroundColor = [UIColor fidelityGreen];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidTimeout:) name:#"ApplicationTimeout" object:nil];
}
- (void) applicationDidTimeout:(NSNotification *) notif
{
NSLog(#"I m here");
BCDSessionInactivityViewController *sessionView=[[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"InactivityViewController"];
sessionView.modalPresentationStyle = UIModalPresentationFormSheet;
sessionView.preferredContentSize = CGSizeMake(838,340);
[[self topViewController] presentViewController:sessionView animated:YES completion:nil];
}
and in logoutviewcontroller, i am removing this observer written below
- (IBAction)logoutbtn:(id)sender
{
NSLog(#"logout is called");
[sessionTimer invalidate];
sessionTimer = nil;
[[BCDTimeManager sharedTimerInstance]stopIdleTimer];
//[self dismissViewControllerAnimated:YES completion:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"ApplicationTimeout" object:nil];
[self performSegueWithIdentifier:#"Thankyoupage" sender:self];
}
This is code where i posting the notification.
- (void)idleTimerExceeded {
NSLog(#"idle time exceeded");
[[NSNotificationCenter defaultCenter]
postNotificationName:#"ApplicationTimeout" object:nil];
}
for first time login, it works fine whenever timer exceeds, i post a notification and model view is presesnted perfectly, but once user logs out, after that whenever the notification is posted, selector method is getting called twice
I am pretty sure that notification is getting posted only once.
Should i create notification in every view controller and then remove it when view unloads?
what i am doing wrong here?
You are adding the notification in RootViewController and trying to remove it from LogoutViewController. So that notification observer added to the RootViewController never gets removed. So each time you logout and login, the observer call will get increased by one. For fixing the issue, you need to remove the observer from the RootViewController object.
For fixing the issue you mentioned in your comment,
If I remove the observer in RootViewController , then if timers
exceeds in some other views, and notification observer is not called.
Also, i can't add observer on app delegate because we want timer
notification to be fired only after reaching rootviewController
Write two public methods in AppDelegate
One for adding observer (addObserver)
One for removing observer (removeObserver)
When you reach RootViewController, call the addObserver method for adding the observer
When logout is pressed, call the removeObserver for removing the observer
I have a UINavigationController hierarchy, VC1 --> VC2.
VC1 has a table view that I need to reload when VC2 is done with its work, so VC1 has this code:
-(void)viewWillAppear:(BOOL)animated
{
[[self tableView] reloadData];
}
VC2 is essentially working with the server to create a new table row in VC1. When the done button in VC2 is pressed, I call [navController popViewControllerAnimated:YES]. So here's what happens from the user's perspective:
Visit VC2, use it to create a new row for the table in VC1. Press done.
The hierarchy successfully navigates back to VC1, but the tableview does not reload and display the new row.
However, if I then nav forward to VC2, and immediately hit the navController back button, the table does reload and show the new row.
So why does [tableview reload] work on 3 but not 2? Thanks so much.
==
More code in response to answer mentioned below:
In App delegate:
CWLandingVC *lvc = [[CWLandingVC alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:lvc];
[[self window] setRootViewController:navController];
In VC0:
-(void)toSessionMgmtViewController
{
TSessionMgmtViewController *tsmvc = [[TSessionMgmtViewController alloc] init];
[[self navigationController] pushViewController:tsmvc animated:YES];
}
In VC1:
- (IBAction)toCreateSessionView:(id)sender
{
TCreateSession *cs = [[TCreateSession alloc] init];
[[self navigationController] pushViewController:cs animated:YES];
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[self tableView] reloadData];
}
In VC2:
Finishes working with server...
UINavigationController *navControler = [self navigationController];
[navControler popViewControllerAnimated:YES];
Also, when VC2 is done working with the server, it updates a data store of TSessions called SessionListStore:
- (TSession *)addSession:(NSString *)code withName:(NSString *) name qs:(int)qs
{
TSession *s = [[TSession alloc] initWithName:name code:code numberQuestions:qs];
[_sessions setObject:s forKey:code];
return s;
}
where sessions is a NSNutatbleDictionary in SessionListStore.
Thanks so much in advance.
EDIT: The solution was to trigger the reloadData call from the completion block of Server call.
Please check this answer,
Popping ViewController doesn't call viewWillAppear when going back
Have you added navigation controller to your view controller or view controllers to your navigation controller?
Also, you can set the desired view controller as the delegate of your navigation controller and implement this method.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
I have a UINavigationController ans a chain of 3 simple controllers. Each one has a button. When press a button a next controller is Pushed. ViewController1 -> ViewController2 -> ViewController3. When I push a back button on the 3rd view i want to move to the first view. Using of backBarButtonItem is obligatory. Here is the code for second controller:
#import "ViewController2.h"
static BOOL isBackButtonPressed;
#implementation ViewController2
- (void)viewDidLoad {
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:#"back from 3" style:UIBarButtonItemStyleBordered target:nil action:nil];
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
if (isBackButtonPressed) {
[self.navigationController popViewControllerAnimated:YES];
} else {
isBackButtonPressed = YES;
}
[super viewWillAppear:animated];
}
#end
But when I press back button on the third view I return to the second view instead of the first view. Could you help me to return to the first view pressing back button on the third view.
I tried suggestions from answers but they don't help.
Adding a selector to backBarButtonItem doesn't help because it is never called.
Adding a [self.navigationController popToRootViewControllerAnimated:YES] in viewWillDisappear methos also doesn't work. I don't know why. I think that the actual problem is how backBarButtonItem works.
Any other suggestions?
The behaviour I try to achieve exists in the calendar on iPhone. When you rotate iPhone to landscape you get to the weeek view. Then go to the event details, and rotate to the portrait. When you press back button you will get to a day view not to a week view, so a controller with weekview is skipped.
After countless number of tries my solution was simply not use backBarButtonItem! As whatever i do it always goes to previous viewController instead of calling its selector
Instead I use only leftBarButtonItem for navigation, as it guarantees calling my action.
Here an example
UIButton *backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 27, 22)];
[backButton setImage:[UIImage imageNamed:#"backbutton"] forState:UIControlStateNormal];
[backButton addTarget:self action:#selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
This certainly calls backButtonPressed action.. This works both for IOS 6 and 7
No need to register a new selector for the back button, just do:
-(void)viewWillDisappear{
if ( [self.navigationController.viewControllers containsObject:self] )
//It means that the view controller was popped (back button pressed or whatever)
//so we'll just pop one more view controller
[self.navigationController popViewControllerAnimated:NO];
}
in your ViewController3 viewWillDisappear method
Try using this in your third view controller, this way you check if you have pressed the back button and directly pop to the root view controller which as you stated is your first view controller.
-(void) viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
[self.navigationController popToRootViewControllerAnimated:YES];
}
[super viewWillDisappear:animated];
}
I had the same problem as you beofre and fixed it like this:
You can capture the back button on the ViewController3 and before poping the view, remove ViewController2 from the navigation stack like this:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:#"back from 3" style:UIBarButtonItemStyleBordered target:self action:#selector(customBackPressed:)];
}
-(void)customBackPressed:(id)sender {
NSMutableArray *allViewControllers = [NSMutableArray arrayWithArray: navigationController.viewControllers];
for (UIViewController *vc in viewControllers)
{
// If vc is ViewController2 type, remove it
}
navigationController.viewControllers = allViewControllers;
[self.navigationController popViewControllerAnimated:YES];
}
Because ViewController2 is not in the stack anymore, it will jump to ViewController1.
Also, if ViewController1 is the root view controller, you can just do:
[self.navigationController popToRootViewControllerAnimated:YES];
I have a tab bar item which is connected navigation controller with a UIViewController as the root view controller. The first touch on the tab bar item switches to that view. The second touch pops to the root view controller. The third touch does not scroll to the top.
I've seen this scroll-to-top behavior in other apps, but after searching the webs, I cannot find out anything about it.
Is this default behavior for scroll views or table views attached to tab bar items, or is it something I need to implement myself?
I realize this is an older question, but I'm also looking to create this behavior, and I think I have a simpler solution.
First, set your AppDelegate to be the delegate for your UITabBarController. Then add this method to AppDelegate.m:
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController {
if ([tabBarController.viewControllers objectAtIndex:tabBarController.selectedIndex]==viewController)
{
if ([viewController isKindOfClass:[UITableViewController class]])
{
[[(UITableViewController *)viewController tableView] setContentOffset:CGPointZero animated:YES];
}
else if ([viewController isKindOfClass:[UINavigationController class]])
{
UINavigationController *nav = (UINavigationController *)viewController;
if ([nav.visibleViewController isKindOfClass:[UITableViewController class]])
[[(UITableViewController *)nav.visibleViewController tableView] setContentOffset:CGPointZero animated:YES];
}
}
return YES;
}
This works if your tab points at a UITableViewController or at a UINavigationController with a UITableViewController as the root view, and you don't have to worry about distinguishing between which UITableViewController is affected, sending notifications, etc.
Here is the solution to scroll to top of the table view when tab bar is clicked
In AppDelegate set tabbar delegate
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController {
if (tabBarController.selectedIndex == 0) {
UINavigationController *selectedNav = [self.tabBarController.viewControllers objectAtIndex:self.tabBarController.selectedIndex];
UIViewController *currentVC = selectedNav.visibleViewController;
if([currentVC isMemberOfClass:NSClassFromString(#"HomeViewController")])
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"refreshView" object:nil];
}
}
return YES;
}
In HomeViewController.m view did load listen for the notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(refreshView:)
name:#"refreshView"
object:nil];
Refresh method
-(void)refreshView:(NSNotification *) notification{
if (self == self.navigationController.topViewController)
[self.tableView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
}
No, this isn't default behaviour, you have to implement it yourself.
I'd do it by making the application delegate the delegate of the tab bar controller, and implement -tabBarController:didSelectViewController: to post a notification. Listen for that notification in your table view controller and do something like:
if (self == self.navigationController.topViewController)
[self.tableView scrollToTop];
Since your tab controller can only have one delegate, you may want to look at the answer to this question, which describes how to listen for the tap using KVO.
I'm using a UISplitviewController as a template.
action for edit button:
newExViewController *editWindow =[[newExViewController alloc]initWithNibName:#"newExViewController" bundle:nil];
UINavigationController *navBar=[[UINavigationController alloc]initWithRootViewController:editWindow];
navBar.modalPresentationStyle = UIModalPresentationFormSheet;
navBar.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:navBar animated:YES];
[navBar release];
[editWindow release];
navBar has a UIBarButton for saveButton. This is called when you press SaveButton
[self dismissModalViewControllerAnimated:YES];
now is the problem:
any idea how to reload the data for both the main NavigationConteroller and the detailViewController when the modalView is dismissed??
I have no clue
thnx
You should look into NSNotificationCenter. In your view with the UITableView, create the notification listener. Then in the view that dismisses, call that notification.
To be more specific, the Notification will call a method that should contain reloadData.
Example
The following should go with the UITableView you want to reload:
This could go along with your [self dismissModalViewControllerAnimated:YES];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(someMethodToReloadTable) name:#"reloadTable" object:nil];
This is how you will call the notification center to reload the table:
[[NSNotificationCenter defaultCenter] postNotificationName:#"reloadTable" object:self];
Example of the notification method:
- (void)someMethodToReloadTable:(NSNotification *)notification
{
[myTableView reloadData];
}
And don't forget to remove the notificaiton observer:
-(void)viewDidUnload
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"reloadTable" object:nil];
}
In controllers, that contains view you want to reload, you should decline following method which will be called when the modalView will be dismissed (or when controller's main view will be first time loaded):
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// here you can reload needful views, for example, tableView:
[tableView reloadData];
}