I'm having a hard time finding the answer to this question I assume is not that hard.
How can I reference methods and properties defined on a viewcontroller from a SKScene ?
And building on that: How can you reference the ViewController from a SKScene that was loaded from within another SKScene?
A better option than my "back reference" answer is to use a protocol to explicitly define the methods that a SKScene
subclass expects the UIViewController
subclass to implement. Here are the steps you'll need to go through.
1) Define the protocol (contract) that the UIViewController
will need to adopt and conform to:
protocol GameManager{
func loadHomeScene()
func loadGameScene(level:Int)
func loadHighScoreScene()
}
2) The UIViewController
subclass now adopts the protocol:
class GameViewController: UIViewController,GameManager{
3) The GameScene
gets an ivar that references the UIViewController
:
var gameManager:GameManager?
4) When the GameManager
needs to call the UIViewController
, it does so like this:
gameManager.loadHighScoreScene()
We now have a well-defined contract of what a GameManager
is. Any other developers (and the current developer's future self) that work on this project now know which methods a GameManager
should implement. This means we could easily port this code to a different project (and thus a different UIViewController
) and it would still work as long as this new UIViewController
adopted the GameManager
protocol.
Similarly, if we decided to move our scene navigation code out of the view controller, and instead placed it in a singleton class, this singleton class would merely need to adopt the GameManager
protocol.
This thread gives 4 approaches to solving the problem, and I have personally used all 4. Here's my take on them:
1) Hard-coding a call to the UIViewController
subclass:
(self.view!.window!.rootViewController as! GameViewController).loadGameScene(1)
Advantage: 1 line of code!
Disadvantage: A very tight coupling of 2 classes, without an explicit contract.
2) Using a pointer back to the UIViewController
:
Advantages: Very little code required, No protocol required.
Disadvantages: Tight coupling of 2 classes. No explicit contract.
3) Using NSNotificationCenter
:
Advantage: Loosely coupled classes.
Disadvantages: more code needed to set up the broadcaster, receiver, and callback methods. Harder to trace and debug the code. No explicit contract. The "one-to many" capability of notifications is nice, but doesn't help us here.
4) Creating a protocol:
Advantages: An explicit contract. The gods of MVC will be pleased.
Disadvantage: More code to write (but much less than notifications).
You should avoid to reference UIViewController
from SKScene
, because it breaks MVC pattern.
As an alternative way you can use NSNotificationCenter
to notify UIViewController
about high score:
In ViewController
:
- (void)awakeFromNib {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(reportScore:)
name:@"ReportScore"
object:nil];
}
-(void)reportScore:(NSNotification *) notification {
NSDictionary *userInfo = [notification userInfo];
NSNumber *scores = (NSNumber *)[userInfo objectForKey:@"scores"];
// do your stuff with GameCenter....
}
- (void) dealloc
{
// If you don't remove yourself as an observer, the Notification Center
// will continue to try and send notification objects to the deallocated
// object.
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
In SKScene
:
- (void)gameOver {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:
[NSNumber numberWithInt:self.scores forKey:@"scores"];
[[NSNotificationCenter defaultCenter]
postNotificationName:@"ReportScore" object:self userInfo:userInfo];
}
in theory you should not do that but in practice...
self.view.window.rootViewController
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With