Suppose, in my xiphone project on Xcode, I have one splash screen "a", 1st view "b", 2nd view "c", a button "back" on 2nd view. Now my task is, when my app will launch at the first time, splash screen will be launched. then "b" will be shown. Then go to "c". After that when again I will launch the app, the view "c" will be shown. And if I want to see the view "b", I have to press "back" button on view "c". Otherwise view "b" will never been shown.
This is my problem.
how could I solve the problem?
You can use notifications. Thus, in view controller C, you can:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// add notification to be informed if the app becomes active while
// this view controller is active, and if so, to invoke the
// appDidBecomeActive method.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(appDidBecomeActive)
name:UIApplicationDidBecomeActiveNotification
object:nil];
}
- (void)appDidBecomeActive
{
// do whatever you want in this method. for example, if you did
// a modal segue (or presentViewController) to present this
// view controller's view, then you'd dismiss like thus:
[self dismissViewControllerAnimated:NO completion:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
// when this view disappears, we should remove the observer on
// the UIApplicationDidBecomeActiveNotification
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIApplicationDidBecomeActiveNotification
object:nil];
}
Related
Scenario:
I need to show 3 or more popups one after the other on button click in each popup. I have created a different viewcontroller and xib files for each popup. So for displaying each popup I have used presentViewController instead of pushViewController.
That is, I have used this:
[self presentPopupViewController:searchPopUpView animationType:0];
instead of
[self.navigationController pushViewController:searchPopUpView animated:YES];
For dismissing a popup, the following code has been written:
[self dismissPopupViewControllerWithanimationType:0];
Issue:
The popups are displaying perfectly, but the background gets darker and darker whenever a popup shows up. After all popups have been dismissed I have to finally click on the blank screen to remove those darker parts. How to overcome this issue?
I think you are using MJPopupViewController to show pop-up.
If it is so, Then try this.
Suppose there is a controllerA from which you want to show a pop-up controller popupControllerB.
Then in your controllerA add Notifications Observer
Code to write in controllerA :
// Add Notification Observer when your view initialise.
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(dismissPopup) name:#"DISMISS_POPUP" object:nil];
In viewWillDisappear remove the notifications observer
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
This method will be called when you Post-notification from your popupControllerB
-(void)dismissPopup {
[self dismissPopupViewControllerWithanimationType:MJPopupViewAnimationFade];
}
And In popupControllerB, Where you want to dismiss the Pop-up, write this code.
[[NSNotificationCenter defaultCenter] postNotificationName:#"DISMISS_POPUP" object:nil];
Above line of code will call a method written in your controllerA and dismiss the pop-up properly.
If you want to dismiss presented UIViewControllers you can use this code. I have used this approach to dismiss presentedViewControllers. It will dismiss all your presentedViewControllers on your rootViewController.
UIViewController* presVC = self.window.rootViewController;
while (presVC) {
UIViewController* temp = vc.presentingViewController;
if (!temp.presentedViewController) {
[vc dismissViewControllerAnimated:NO completion:^{}];
break;
}
vc = temp;
}
I'm using the below notifications to reload ViewControllerA when my app comes back from background mode. It works correctly, but the applicationEnteredForeground: method gets called every time when I open the app again. For example if I close the app when ViewControllerB or ViewControllerC is on the screen and open it again the method will be called despite the viewDidLoad of ViewControllerB doesn't contain applicationEnteredForeground: method. I would like to know that how could solve this issue? My goal is to use applicationEnteredForeground: only when ViewControllerA was on the screen before I closed the app.
As a possible solution I would just remove the NSNotificationCenter in the viewDidDisappear, but since the observer is in the viewDidLoad it won't work when the user navigates back, because viewDidLoad won't be called again. Is there any fix for this?
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationEnteredForeground:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
}
- (void)applicationEnteredForeground:(NSNotification *)notification {
// do stuff...
}
You should remove ViewController A's event listener on viewWillDisappear and add it in viewWillAppear. That way, VC A will only be listening when it is the visible view controller.
You can check if a view controller is on screen by checking the window property of it's view. It will work in most standard cases.
- (void)applicationEnteredForeground:(NSNotification *)notification
{
if (self.view.window == nil) {
// Not on screen
return;
}
// do stuff...
}
I have a table view inside a view controller. When i want to add something to my tableview i press the add-button and i go to a detail view controller where i have a save button. Now I want to go back to my tableviewcontroller and refresh it after pressing my save button.
I'm sending my data to an external database and not in the core data.
Since you are using modalViewController, after you tap Save button, you may dismiss it and to refresh the data, you will need to Use NSNotification in this case.
Try the following code:-
Inside the view controller that contains the tableview,
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(tableReload) name:#"tableReload" object:nil];
}
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"tableReload" object:nil];
}
-(void)tableReload{
[self.tableView reloadData];
}
In the second view controller, IBAction for the save button
[[NSNotificationCenter defaultCenter]postNotificationName:#"tableReload" object:nil];
[self dismissViewControllerAnimated:YES completion:nil];
I have a UITabBarController, which has 4 tabs. Each one of those tabs is a separate UIViewController. I have objects on each one of those 4 VC's that use NSNotification's to perform actions upon the press of a certain object. The 4 VC's all respond to the notification in the same way because it is a similar object on each page. When this object is pressed it presents a view onto the current view controller. The problem is that if I move to any of the other 3 tabs now that view is also on their VC. That is because the notification is being responded to on all 4 tabs when it is pressed on any of the VC's. I am needing it to only respond to the VC that the user is currently on and not any of the others that are in the tab bar.
Is there a way to get this to work properly? Maybe a threshold where you can set how many times the notification can perform its selector after being called? That way I could set it to 1 and at any given time if that notification is called the selector can only be called 1 time.
The type of object implementation that I'm using requires me to use NSNotification's so there is no way to change how I interact.
edit:
This viewDidLoad method is on the top level VC for the 4 VC's in my tab bar. All 4 of them either use this directly or inherit from it.
- (void) viewDidLoad
{
...
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didSelectItemFromCollectionView:) name:#"didSelectItemFromCollectionView" object:nil];
}
Action Handler:
- (void) didSelectItemFromCollectionView:(NSNotification *)notification
{
NSDictionary *cellData = [notification object];
if (cellData)
{
NewVC *pushToVC = [self.storyboard instantiateViewControllerWithIdentifier:#"PushToVC"];
[self.navigationController pushViewController:pushToVC animated:YES];
}
}
Each of the 4 VC's is a UITableViewController and have cells with an object that can be pressed. This NSNotificationCenter action is what allows the operation to work.
You must have implemented the NSNotificationCenter's -addObserver:selector:name:object: method in the -viewDidLoad of every viewController
Example:
- (void)viewDidLoad
{
//...
[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(doSomething:)
name:#"TestNotification"
object:nil];
}
Instead of having this in -viewDidLoad, move it within -viewWillAppear and implement removeObserver:name:object: in -viewWillDisappear.
This way, only the viewController that is currently on will respond to the notification.
Example:
- (void)viewWillAppear:(BOOL)animated
{
//...
[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(doSomething:)
name:#"TestNotification"
object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
//...
[NSNotificationCenter defaultCenter] removeObserver:self
name:#"TestNotification"
object:nil];
}
- (void)doSomething:(NSNotification *)userInfo
{
//...
//if you push a viewController then the following is all you need
[self.navigationController pushViewController:vcSomething
animated:YES];
//however.... if you're instead presenting a viewController modally then
//you should implement "-removeObserver:name:object: in this method as well
//[NSNotificationCenter defaultCenter] removeObserver:self
// name:#"TestNotification"
// object:nil];
//[self presentViewController:vcSomething
// animated:YES
// completion:nil];
//OR... in the completion parameter as:
//[self presentViewController:vcSomething
// animated:YES
// completion:^{
// [NSNotificationCenter defaultCenter] removeObserver:self
// name:#"TestNotification"
// object:nil];
// }];
}
EDIT:
You (#Jonathan) commented:
I really appreciate your answer and it has helped me out a lot! I
actually ran into one more scenario where this issue occur's and I'm
not sure how to figure it out. Right now I have a VC that presents
another VC modally. Each one has observers for the same
NSNotification. Everything performs perfectly well when I'm in the
modally presented VC, but once I dismiss that VC and return to the
underlying one I have the same issue where the notification is being
called multiple times. Do you have an idea for a solution in this
case?
Now... regarding this...
FIRSTLY... NOTE:
Multiple -addObserver:selector:name:object: will register the specified notification multiple times (means... same notification being registered for N times will call invoke the target selector N times)
Presenting a ViewController (call it Child) from, say, Parent viewController will NOT invoke the -viewWillDisappear: of the Parent
where as...
Dismissing the Child viewController will still invoke -viewWillAppear: of the Parent
This creates an imbalance in the logic and if not handled (as per the commented lines in the code example of the doSomething method above), it results in the Parent registering for the notification multiple times (as it's -viewWillAppear: method is called more often than not -viewWillDisappear:)
also see: similar question
it should be called only once so that it never gets called again
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(methodName:) name:#"name" object:nil];
});
I'm working on my embedded table views. They should scroll up when the keyboard hides some textfields. But I have several view controllers in my navigation view controller with this behaviour. So far my code registration and unregistration code is:
- (void)viewDidLoad
{
[super viewDidLoad];
[self registerForKeyboardNotifications];
}
// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
But if I'm showing the keyboard in let's say view controller number 3 in my navigation controller, the keyboardWasShown method is called three times. Doesn't this matter or do I need to unregister everytime the viewWillDisappear?
You should move [self registerForKeyboardNotifications]; to viewDidAppear and unregister in viewDidDisappear.
By registering in viewDidLoad and unregistering in dealloc, especially within a navigation controller, the notification will fire once for every view controller on the navigation stack. You only need to call it for the currently visible view.
Alternatively, you could subclass the navigation controller and have it call a method on its currently visible controller. Then you don't have to do all this registering and unregistering. Just register once in a nav controller subclass and have that class pass the message along to the proper view controller.