Reputation: 1772
I am using NSNotificationCenter to notify when keys are pressed on the keyboard. When moving between scenes, if an additional key is pressed too quickly after the key that causes the scene transition is pressed the app crashes. I'm not sure if it's the previous scene that no longer receives notifications or if the next scene's observer for notifications is not set up. What can I do to stop this from happening? Here's the code for two different scenes and the custom view that handles the notifications. Essentially, I'm posting a notification for key presses in CustomSKView
and then I handle the presses in the respective scenes in a method called keyPressed:
that is not listed here.
LevelSelectScene.m
@implementation LevelSelectScene
-(void)didMoveToView:(SKView *)view {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyPressed:) name:@"KeyPressedNotificationKey" object:nil];
//perform scene setup here
...
}
-(void)willMoveFromView:(SKView *)view {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:@"KeyPressedNotificationKey"
object:nil];
//perform additional cleanup before moving to next scene
...
}
Menu.m
-(void) didMoveToView:(SKView *)view {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyPressed:) name:@"KeyPressedNotificationKey" object:nil];
//perform menu setup here
...
}
-(void) willMoveFromView:(SKView *)view {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:@"KeyPressedNotificationKey"
object:nil];
//perform additional cleanup before moving to next scene
...
}
CustomSKView.m
#import "CustomSKView.h"
@implementation CustomSKView:SKView {
}
- (id) initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
return self;
}
- (void) keyDown:(NSEvent *)theEvent {
[[NSNotificationCenter defaultCenter] postNotificationName:@"KeyPressedNotificationKey"
object:nil
userInfo:@{@"keyCode" : @(theEvent.keyCode)}];
}
@end
EDIT: Stack Trace
2015-08-15 05:47:08.199 PianoKeyboardTest[21854:4643404] -[NSPathStore2 keyPressed:]: unrecognized selector sent to instance 0x10050d110
2015-08-15 05:47:08.199 PianoKeyboardTest[21854:4643404] -[NSPathStore2 keyPressed:]: unrecognized selector sent to instance 0x10050d110
2015-08-15 05:47:08.200 PianoKeyboardTest[21854:4643404] (
0 CoreFoundation 0x00007fff8575803c __exceptionPreprocess + 172
1 libobjc.A.dylib 0x00007fff9227376e objc_exception_throw + 43
2 CoreFoundation 0x00007fff8575b0ad -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
3 CoreFoundation 0x00007fff856a0e24 ___forwarding___ + 1028
4 CoreFoundation 0x00007fff856a0998 _CF_forwarding_prep_0 + 120
5 CoreFoundation 0x00007fff8571445c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
6 CoreFoundation 0x00007fff85604634 _CFXNotificationPost + 3140
7 Foundation 0x00007fff83e8e9d1 -[NSNotificationCenter postNotificationName:object:userInfo:] + 66
8 PianoKeyboardTest 0x000000010001d50e -[CustomSKView keyDown:] + 270
9 AppKit 0x00007fff8ba1c11b -[NSWindow _reallySendEvent:isDelayedEvent:] + 5452
10 AppKit 0x00007fff8b3add76 -[NSWindow sendEvent:] + 470
11 AppKit 0x00007fff8b3aa9b1 -[NSApplication sendEvent:] + 4199
12 AppKit 0x00007fff8b2d3c68 -[NSApplication run] + 711
13 AppKit 0x00007fff8b250354 NSApplicationMain + 1832
14 PianoKeyboardTest 0x0000000100005322 main + 34
15 libdyld.dylib 0x00007fff8f1ee5c9 start + 1
)
EDIT: Solution
Here are the changes that I made to CustomSKView.
#import "CustomSKView.h"
@implementation CustomSKView:SKView {
// Add instance variables here
}
- (id) initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
// Allocate and initialize your instance variables here
}
return self;
}
- (void) keyDown:(NSEvent *)theEvent {
[[NSNotificationCenter defaultCenter] postNotificationName:@"KeyPressedNotificationKey"
object:nil
userInfo:@{@"keyCode" : @(theEvent.keyCode)}];
}
//overridden version of SKScene's presentScene: transition: method
-(void) presentScene:(SKScene *)scene transition:(SKTransition *)transition {
[[NSNotificationCenter defaultCenter] removeObserver:self.scene
name:@"KeyPressedNotificationKey"
object:nil];
[super presentScene:scene transition:transition];
}
@end
Upvotes: 2
Views: 266
Reputation: 12753
To remove an observer from the current SKScene
whenever the game transitions to a new scene, override the presentScene
method in your custom view class, remove the observer, and then call the super class's presentScene
:
- (void) presentScene:(SKScene *)scene transition:(SKTransition *)transition {
[[NSNotificationCenter defaultCenter] removeObserver:self.scene
name:@"KeyPressedNotificationKey"
object:nil];
[super presentScene:scene transition:transition];
}
Upvotes: 1
Reputation: 122391
The issue appears to be timing. You need to remove the notification before the transition starts, however SKView
does not provide a convenient hook for this.
One possible way of managing this is to subclass SKView
to provide the mechanism to add and remove the observers, with the possibility of using multiple notifications; one for keystrokes and the other for transition start/ends. When a transition start notification is fired, this subclass will remove the keystroke observer. When the transition has finished it could be notified to re-observe keystrokes. However this does sound complicated.
Upvotes: 1