I have several issues about NSNotification, and lifecycle of its observers.
UPD.
I will simplify my app's logic, so it will look primitive:
The ViewController A has button "Comment" and also contains a UIView B. On this UIView we have another button "Share". Each button does what it should if the user is logged in, if not it calls the "login" method from the NSObject class "Logistic" (where the most logic is) and the popup view C appears.
So I made a postNotificationName in C, to make the buttons listen to if the user logged in - do their job.
e.g. in viewController A
- (void) comment{
if (<user_logged_in>){
//do the magic
[self removeObserver];
} else {
[self removeObserver];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(comment) name:#"dismiss_popup" object:nil];
[Logistic login];
}
}
I do the same for the "Share" method (which is in the view B), but when I for instance press the button "Comment", then skip the C - login popup, then press the "Share" button, do the login - and after that both the "Share" and "Comment" start their actions simultaneously.
I think I should call the removeObserver
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"dismiss_popup" object:nil];
But how to do it in the UIView B while it still exists?
Regards .
You can modify your login method in Logistics as following:
function prototype:
+ (void)loginComplete:(void(^)(BOOL success))complete;
function itself:
+ (void)loginComplete:(void(^)(BOOL success))complete {
//login code
BOOL success = YES;//or no if it was some problems))
complete(success);//replace notification post with this
}
and finally your in viewController A:
- (void) comment{
if (<user_logged_in>){
//do the magic
[self removeObserver];
} else {
[self removeObserver];
//we don't need Notification center anymore
[Logistic loginСomplete:^(BOOL success) {
handle login completion
}];
}
}
- (IBAction)commentButton:(id)sender {
if (!user logged in) {
[self.logistic loginUserWithCompletion:^(id result){
if (![result isKindOfClass:[NSError class]]) {
[self doStuff];
}
}];
} else {
[self doStuff];
}
To recap your problem:
A is a parent of B.
A and B call C.
A and B listen to C.
When C send notification both A and B respond but you only want one of them
to respond.
I suggest letting A handle all communication with C, so if B need to talk to C it needs to go through A. This is how you can do this:
in UiViewController A
-(void) viewDidLoad{
//Adding a notification listener should be done in initialization stage.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(dismissViewC:)
name:#"dismiss_popup" object:nil];
}
- (void) comment{
if (<user_logged_in>){
//proceed with comment
} else {
NSNumber* caller=[NSNumber numberWithInt:COMMENT];
[Logistic login:caller];
}
}
-(void) dismissViewC:(NSNotification*) notify{
(NSNumber*) callerId =[notify object];
switch(callerId.intValue){
case COMMENT: //proceed with comment;
case: SHARE: [self.viewB share];
}
}
-(void) dealloc {
// removing an observer should be done in the dealloc stage
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
in UIView B:
- (void) share{
if (<user_logged_in>){
//proceed with share
} else {
NSNumber* caller=[NSNumber numberWithInt:SHARE];
[Logistic login:caller];
}
}
Logistics will need to pass the new login parameter to C and C will look like this:
-(void) login:(NSNumber*) caller{
self.myCaller=caller;
// proceed with login
}
-(void) dismissMe{
[[NSNotificationCenter defaultCenter] postNotificationName:#"dismiss_popup"
object:self.caller];
}
Related
I am writing a sprite kit game and I have in app purchase in my game. I implement the in
app purchase in file IAPHelper.m(IAPHelper.m is a subclass of NSObject), and if the
purchase success, it will post a nsnotification,the code is like:
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"completeTransaction...");
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
}
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier {
NSLog(#"post a notification");
[[NSNotificationCenter defaultCenter] postNotificationName:IAPHelperProductPurchasedNotification object:productIdentifier userInfo:nil];
}
And in MyScene.m(MyScene.m is a subclass of SKScene), I add an observer for the notification:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ok:) name:IAPHelperProductPurchasedNotification object:nil];
}
return self;
}
- (void)ok:(NSNotification *)notification {
NSLog(#"Ok, I got it");
}
And What I am trying to do is IAPHelper.m post the notification, and MyScene.m receive it and respond to it. But the result is MyScene.m seems not receiving the notification and thus the code
- (void)ok:(NSNotification *)notification {
NSLog(#"Ok, I got it");
}
is not being called. but if I put the observer in my ViewController.m( subclass of UIViewController), it works. My question is: can skscene receive notification from any object? And if it can, how to implement it.
Many Thanks if anyone can help me with this problem.
I want to integrate shake feature throughout the app. So I am doing everything in appDelegate. I need to push a viewController, I am able to push in motionBegan, but i wanted to do it motionEnded. yes motion ended does work in a view controller, but in app delegate it is not being called.
Doing as
- (void)applicationDidBecomeActive:(UIApplication *)application {
[self becomeFirstResponder];
}
- (BOOL)canBecomeFirstResponder{
return YES;
}
motionEnded not called
-(void) motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if(event.subtype==UIEventSubtypeMotionShake){
NSLog(#"motionEnded called");
}
}
motionBegan called
-(void) motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if(event.subtype==UIEventSubtypeMotionShake){
NSLog(#"motionBegan called");
}
}
you could basically register your viewController for applicationDidBecomeActiveNotification or any depending on your needs
for example in your viewController's viewDidLoad method you could register it for notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(myMethod)
name:UIApplicationDidBecomeActiveNotification object:nil];
and implement this method in your class, your myMethod will call everytime your application will become active
-(void) myMethod(){
// do your stuff
}
finally un-register viewController from the notification in dealloc method
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
When I'm doing a half-page curl modal transition:
How can I tell when the page has been restored back to it's current state? I want to call something when the "settings" view has been closed.
I tried to use viewWillAppear:(BOOL)animated but it doesn't seem to get called when closing the view. Any ideas?
You can register an NSNotificationCenter observer on your master view and post the notification on your background view. And instead of viewWillAppear you can use viewDidLoad.
// EDIT: sample code to get a touch gesture in a given rect
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if ([[event allTouches]count] == 1) {
UITouch *t = [[touches allObjects]lastObject];
CGPoint p = [t locationInView:self.view];
if (p.y < 200) NSLog(#"above 200");
}
}
In your viewDidLoad, register a Notification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(updateView:)
name:#"updateRootView"
object:nil];
Now this is the notification that we call
- (void) updateView:(NSNotification *) notification
{
/* notification received after the page is uncurled */
}
The calling method:
- (void) unCurlPage
{
// All instances of TestClass will be notified
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateRootView" object:self];
}
And don't forget to dealloc the notification
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
ViewWilAppear is a UIViewController message/method. If you are only changing views, it won't get called. What does the code you are using to close the settings view look like?
Edit
It sounds like you need to refactor a bit. Assuming all this is handled by the parent UIViewController for this settings view, you could implement something like:
- (void)settingsPanelOpen {
// present the modal
// hook to inform of opening (if necessary)
}
- (void)settingsPanelClose {
// dismiss modal
// hook to inform of closing
}
Then settingsPanelClose could have a hook into it if you need to know when the settings closes.
The other thing you could do is subclass UIViewController as SettingsViewController and override the viewDidDisappear: method to kick off a SettingsDidSave notification or otherwise inform your app that it has closed.
I have notification on movie player:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
And it's handler:
- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
[[UIApplication sharedApplication] setStatusBarHidden:YES];
// Remove observer
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
[self dismissModalViewControllerAnimated:YES];
}
Here in this handler method I want to check if the done button is sender. Because I have two senders to this method. How ti check this?
Per docs: MPMoviePlayerPlaybackDidFinishNotification userInfo dictionary must contain NSNUmber for MPMoviePlayerPlaybackDidFinishReasonUserInfoKey key indicating the reason playback has finished. Its possible values:
enum {
MPMovieFinishReasonPlaybackEnded,
MPMovieFinishReasonPlaybackError,
MPMovieFinishReasonUserExited
};
You will first need to assign tag to your buttons before the action and then check the value of the sender tag.
Just add these lines of code:
- (void) moviePlayBackDidFinish:(NSNotification*)notification {
NSInteger anyInteger = [sender tag];
//Now check the value of the anyInteger and write the code accordingly.
//switch case or if condition whatever you want.
}
That's it.
This is an old thread but I stumbled upon it while looking for a solution, and the accepted solution doesn't show the final code.
Here is what you have to do:
- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
NSLog(#"moviePlayBackDidFinish");
// Remove observer
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
NSInteger movieFinishReason= [[[notification userInfo]objectForKey:
MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue];
if(movieFinishReason == 2 || movieFinishReason == 1 || movieFinishReason == 0){
[self dismissViewControllerAnimated:YES completion:nil];
}
/*
MPMovieFinishReasonPlaybackEnded = 0,//played movie sucessfuly.
MPMovieFinishReasonPlaybackError = 1, //error in playing movie
MPMovieFinishReasonUserExited = 2; //user quitting the application / user pressed done button
*/
}
Add tag with the button and put condition according to the tag.
Or check by
if([sender isEqual:btn1])
{
}
else
{
}
I'd like to reload a table view which is in another class called "WriteIt_MobileAppDelegate" from one of my other classes which is called "Properties". I've tried to do this via the NSNotificationCenter class - the log gets called but the table is never updated.
Properties.h:
[[NSNotificationCenter defaultCenter] postNotificationName:#"NameChanged"
object:[WriteIt_MobileAppDelegate class]
userInfo:nil];
WriteIt_MobileAppDelegate.m
-(void)awakeFromNib {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadItProperties:)
name:#"NameChanged" object:self];
}
- (void) reloadItProperties: (NSNotification *)notification {
NSLog(#"Reloading Data"); //this gets called
[[self navigationController] dismissModalViewControllerAnimated:YES];
[self.navigationController popToRootViewControllerAnimated:YES];
[self.tblSimpleTable reloadData];
[self.tblSimpleTable reloadSectionIndexTitles];
// but the rest doesn't
}
What am I doing wrong here?
Seems like you are using the object parameter wrong:
addObserver:selector:name:object:
notificationSender
The object whose
notifications the observer wants to
receive;
that is, only notifications
sent by this sender are delivered to
the observer. If you pass nil, the
notification center doesn’t use a
notification’s sender to decide
whether to deliver it to the observer.