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.
Related
I have the following snippet of code in viewDidLoad which I post a notification to be received by a different ViewController, and prior to iOS 7 (and XCode 5) this has worked:
if ([PFUser currentUser] && [PFFacebookUtils isLinkedWithUser:[PFUser currentUser]]) {
NSLog(#"Current user exists and is logged in"); //current works, so I know that this if-statement is satisfied
[self performSegueWithIdentifier:#"GoToRatingsView" sender:self];
[[NSNotificationCenter defaultCenter] postNotificationName:#"testSetup" object:nil]; //part in question
}
And in the segued ViewController, I have the following:
(void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(testSetupFunction) name:#"testSetup" object:nil];
}
(void)testSetupFunction
{
NSLog(#"This function executed");
}
Currently, testSetupFunction does not executed, which means the notification is not received. I am unsure whether its because I have segued to a different view and then posted the notification, or that this is something new with iOS 7, but currently the notification is no longer received.
Thanks for your help!
Post the notification once the viewcontroller has time to load.
- (void)postNotificaiton
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"testSetup" object:nil];
}
if ([PFUser currentUser] && [PFFacebookUtils isLinkedWithUser:[PFUser currentUser]]) {
[self performSegueWithIdentifier:#"GoToRatingsView" sender:self];
[self performSelector:#selector(postNotification) withObject:nil afterDelay:0.0];
}
You could also post a notification from your segued View Controller saying that it was loaded, and in response to that notification, post your testSetup notification.
did you try adding the observer in the init method
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(testSetupFunction) name:#"testSetup" object:nil];
// Custom initialization
}
return self;
}
OR You can also try to put a log in viewDidLoad to check actually the view is loaded at that time, sometimes it takes time to load the view, view is only loaded only when it is needed.
i want to reload table when app comes to foreground from background, in my app delegate.m i did like this, but its not working
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSLog(#"applicationWillEnterForeground");
[[NSNotificationCenter defaultCenter] postNotificationName:#"EnteredForeground"
object:nil];
}
and in my viewController i am working like
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(whenAppEnteredIntoForeground:) name:#"EnteredForeground" object:nil];
}
- (void)whenAppEnteredIntoForeground:(id)object {
NSLog(#"log msg");
[tblSearch reloadData];
}
what should i do? what mistake i am doing? any help please
Firstly, you don't need to rebroadcast a notification when the app comes to the foreground, you can register for the notification from your view controller.
In your case, it's likely to be that the view is not loaded until after your secondary notification is sent, which is why your view controller cannot respond to it. Using breakpoints will confirm if this is the case.
Use this instead:
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(whenAppEnteredIntoForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
}
And you don't need to rebroadcast a notification from your appdelegate.
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appEnteredIntoForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
}
- (void)appEnteredIntoForeground:(id)object {
[tableView reloadData];
}
Make sure that your Reload Data must call from main Thread Otherwise it will not be reloaded.
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];
}
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];
}
what method within a ViewController's class can I call to check when it has been brought to the foreground?
For example Im looking at a page on my application and I decide to close the application and go back to it later. When I go back to it the same view as I was looking at was on the screen. However... As soon as I open the application I want to segue over to another view.
How can I do this?
Currently trying this:
- (void) applicationDidBecomeActive:(NSNotification*) notification
{
[self checkActivity];
// Do your stuff here
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillEnterForeground:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
}
return self;
}
- (void)checkActivity{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSLog(#"Checking if re-authentication required...");
if([[defaults objectForKey:#"shouldgotologin"] isEqualToString:#"yes"]){
NSLog(#"View Should go to login...performing segue");
[defaults setObject:#"no" forKey:#"shouldgotologin"];
[defaults synchronize];
[self performSegueWithIdentifier:#"backtologin" sender:self];
} else {
NSLog(#"Should go to login is not true.");
}
}
Register your view controller to observe UIApplicationWillEnterForegroundNotification:
1) Inside view controller's init method:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillEnterForeground:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
2) Inside view controller's dealloc method:
[[NSNotificationCenter defaultCenter] removeObserver:self];
3) Also, have your view controller implement this method:
- (void) applicationWillEnterForeground:(NSNotification*) notification
{
// This method will be called just before entering the foreground;
// Do your stuff here
}
If the timing of UIApplicationWillEnterForegroundNotification doesn't suit you, check all the available notifications for UIApplication here:
http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html
Jump To ApplicationDelegate File, you will find following methods.
- (void)applicationWillResignActive:(UIApplication *)application
{
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
}
Remember, It is not the viewController who receives the notifications related to the Application states like willResignActive,didEnterBackground,willEnterForeground. ApplicationDelegate object is going to handle those notifications. So, Try putting your logic in above methods.
Hope that helps. If not, add your queries using comments below my answer.