Passing Data to SKScene from UIVIewController - ios

What I am trying to accomplish is (should be at least..) very simple: I want to present an SKScene with data that I have in the UIViewController that is presenting this scene. In other words, I have a variable chapter declared as the following:
#import <UIKit/UIKit.h>
#import <SpriteKit/SpriteKit.h>
#interface ViewController : UIViewController
#property (nonatomic) NSInteger chapter;
#end
I have tested with logs that the variable chapter is in fact declared (in my case i am just testing with it as 4).
Here is my ViewController.m file and how I am presenting my scene:
- (void)viewDidLoad {
[super viewDidLoad];
.
.
.
// Configure the view.
SKView *skView = (SKView *)self.view;
if (!skView.scene) {
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
scene = [OLevel sceneWithSize:CGSizeMake(skView.bounds.size.width, skView.bounds.size.height)];
scene.scaleMode = SKSceneScaleModeResizeFill;
// Set the SKScene chapter #
scene.chapter = self.chapter; // <<<< NOT WORKING?
NSLog(#"CHAP TO PASS: %u", self.chapter);
NSLog(#"CHAP RECEIVED: %u", scene.chapter);
// Present the scene.
[skView presentScene:scene];
}
With scene defined as the following, with the viewDidLoad being the one shown above: (I have it subclassed)
#interface ViewController () {
OLevel *scene;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
.
.
.
Here is my OLevel.h class:
#import <SpriteKit/SpriteKit.h>
#import <GameplayKit/GameplayKit.h>
#interface OLevel : SKScene <SKPhysicsContactDelegate, GKAgentDelegate>
#property (nonatomic) NSInteger chapter;
#end
And my OLevel.m*:
- (id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
NSLog(#"Creating scene");
NSLog(#"CHAP: %u", self.chapter);
[self setUpScene];
}
return self;
}
I am very much confused as to why this approach is not working. I have set the property in my ViewController to the desired chapter value, I have then told my Olevel what value it is and to set it's own chapter property to such value, and then present the scene correctly. Why do I continue to get 0??
2016-07-18 23:52:32.189 TESTAPP[2038:684620] CHAP TO PASS: 4
2016-07-18 23:52:32.193 TESTAPP[2038:684620] CHAP RECEIVED: 0
2016-07-18 23:52:32.195 TESTAPP[2038:684620] Creating scene
2016-07-18 23:52:32.195 TESTAPP[2038:684620] CHAP: 0
I know this question has been answered here with the same approach that I had originally used before looking for an answer, but I have yet to get it to work..
If anyone knows what I am doing wrong please help me.

You have initialize your scene with the wrong syntax, without type:
...
// Create and configure the scene.
OLevel *scene = [OLevel sceneWithSize:CGSizeMake(skView.bounds.size.width, skView.bounds.size.height)];
scene.scaleMode = SKSceneScaleModeResizeFill;
// Set the SKScene chapter #
scene.chapter = self.chapter;
...
If you want you can also subclass init method with chapter
OLevel.h:
#interface GameScene : SKScene <SKPhysicsContactDelegate, GKAgentDelegate>
#property (nonatomic) NSInteger chapter;
-(id)initWithSize:(CGSize)size chapter:(NSInteger)chapter;
+(id)sceneWithSize:(CGSize)size chapter:(NSInteger)chapter;
#end
OLevel.m:
#import "GameScene.h"
#implementation GameScene
-(id)initWithSize:(CGSize)size chapter:(NSInteger)chapter {
if (self = [super initWithSize:size]) {
self.chapter = chapter;
//some custom code here
NSLog(#"Creating scene");
NSLog(#"CHAP: %ld", (long)self.chapter);
}
return self;
}
+(id)sceneWithSize:(CGSize)size chapter:(NSInteger)chapter {
return ((GameScene *)[[self alloc] initWithSize:size chapter:chapter]);
}
-(void)didMoveToView:(SKView *)view {
...
}
}
In this case you can create your scene as:
// Create and configure the scene.
scene = [OLevel sceneWithSize:CGSizeMake(skView.bounds.size.width, skView.bounds.size.height) chapter:self.chapter];
scene.scaleMode = SKSceneScaleModeResizeFill;

You can also try to pass your data using the NSNotificationCenter
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveTestNotification:)
name:#"TestNotification"
object:nil];
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:myObject forKey:#"someKey"];
[[NSNotificationCenter defaultCenter] postNotificationName: #"TestNotification" object:nil userInfo:userInfo];

Related

view's frame not releasing its memory

I'm not able to rectify the situation which is arising in
customTabView.view.frame = CGRectMake(0, self.view.frame.size.height-49, self.view.frame.size.width, 49);
where, memory assigned every time and not releasing after dismissing the view.
Well, the situation is that, I have created a simple login screen as rootviewcontroller, then through a button (login button) presenting a tabviewcontroller where, I have added a view as subview on tabviewcontroller, on that subview four buttons are there, through delegate I'm controlling the tabIndex, everything is working fine, but when I check about the leaking memory, above view.frame keeping the memory.
I'm posting the image, please look at this,
Here is my TabView Controller with custome tabbar.
Any help will be appreciable.
UPDATE:
Here is my complete code.
From Login Page:
- (IBAction)clickLoginBtnAction:(id)sender {
tabControler = [self.storyboard instantiateViewControllerWithIdentifier:#"TabBarController"];
[self presentViewController:tabControler animated:YES completion:nil];
}
From Tabbar Controller:
#import "TabBarController.h"
#interface TabBarController ()<TabBarIndexProtocol>{
// CustomTabViewController *customTabView;
}
#property (strong) CustomTabViewController *customTabView;
#end
#implementation TabBarController
#synthesize customTabView;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
customTabView = [self.storyboard instantiateViewControllerWithIdentifier:#"CustomTabViewController"];
self.tabBar.hidden = YES;
[self frameCustomTabViewOnTabBar];
customTabView.tabBarIndexDelegate = self;
}
-(void)viewDidDisappear:(BOOL)animated{
// customTabView.view.frame = nil;
}
// For Screen Rotation
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
[self frameCustomTabViewOnTabBar];
}];
}
-(void)frameCustomTabViewOnTabBar{
customTabView.view.frame = CGRectMake(0, self.view.frame.size.height-49, self.view.frame.size.width, 49);
[self.view addSubview:customTabView.view];
}
# pragma mark - TabBarIndexProtocol from CustomTabBarClass
-(void)tabBarIndexSelected :(int)tabNumber{
NSLog(#"%d",(int)tabNumber);
[self setSelectedIndex:tabNumber];
}
In My CustomeTab.h File:
#import <UIKit/UIKit.h>
#protocol TabBarIndexProtocol <NSObject>
#required
-(void)tabBarIndexSelected :(int)tabNumber;
#end
#interface CustomTabViewController : UIViewController
#property(weak) id <TabBarIndexProtocol> tabBarIndexDelegate;
#end
and from its .m file
#import "CustomTabViewController.h"
#interface CustomTabViewController ()
#end
#implementation CustomTabViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (IBAction)tabNumberOne:(id)sender {
[self.tabBarIndexDelegate tabBarIndexSelected:0];
}
- (IBAction)tabNumberTwo:(id)sender {
[self.tabBarIndexDelegate tabBarIndexSelected:1];
}
- (IBAction)tabNumberThree:(id)sender {
[self.tabBarIndexDelegate tabBarIndexSelected:2];
}
- (IBAction)tabNumberFour:(id)sender {
[self.tabBarIndexDelegate tabBarIndexSelected:3];
}
Thats all, where I'm mistaking :( .

Change UILabel text from subview using delegate

I have a view with a label in it. The lower side of this view is filled with a subview which has it’s own view controller. This subview has a button. My goal: I want to change the label by pressing the button in the subview.
I have tried the code beneath. It works fine when I let the ViewWillAppear method of the delegate call the ‘doSomeTask’ method of the SubviewController. A timer begins to count and after a few seconds, the SubviewController calls the delegates changeLabel method, and the label is changed. However, when I call the ‘doSomeTask’ method manually by pressing the button, the delegate isn’t listening. The call doesn’t reach the changeLabel method.
Probably I am doing something ridiculously stupid here. Forgive me, I’m just learning iOS programming. Thanks in advance for all the feedback.
MyViewController.h
#import <UIKit/UIKit.h>
#import "MySubViewController.h"
#interface MyViewController : UIViewController
#property (strong, nonatomic) IBOutlet UIView *detailView;
#property (strong, nonatomic) IBOutlet UILabel *theLabel;
- (IBAction)changeLabel:(id)sender;
#end
MyViewController.m
#import "MyViewController.h"
#import "MySubViewcontroller.h"
#interface MyViewController ()
#end
#implementation MyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
MySubViewController *subVC = [[MySubViewController alloc] init];
[self addChildViewController:subVC];
[self.detailView addSubview:subVC.view];
// [subVC setDelegate:self]; (no success trying this)
[subVC didMoveToParentViewController:self];
}
-(void)viewWillAppear:(BOOL)animated
{
MySubViewController *mySubVC = [[MySubViewController alloc] init];
[mySubVC setDelegate:self];
[mySubVC doSomeTask]; // this works fine
}
// delegate method
- (void)changeLabel{
self.theLabel.text = #"Hello!";
}
MySubviewController.h
#import <UIKit/UIKit.h>
#protocol MySubViewControllerDelegate <NSObject>
#required
- (void)changeLabel;
#end
#interface MySubViewController : UIViewController
#property (nonatomic, assign) id delegate;
- (IBAction)changeButton:(id)sender;
- (void)doSomeTask;
- (void)goChangeLabel;
#end
MySubViewController.m
#import "MySubViewController.h"
#interface MySubViewController ()
#end
#implementation MySubViewController
#synthesize delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)goChangeLabel
{
[[self delegate] changeLabel];
}
- (void)doSomeTask
{
[NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(goChangeLabel) userInfo:nil repeats:NO];
}
- (IBAction)changeButton:(id)sender {
// [self doSomeTask]; (no succes trying this)
}
#end
Your problem is this:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
MySubViewController *subVC = [[MySubViewController alloc] init];
[self addChildViewController:subVC];
[self.detailView addSubview:subVC.view];
// [subVC setDelegate:self]; (no success trying this)
[subVC didMoveToParentViewController:self];
}
-(void)viewWillAppear:(BOOL)animated
{
MySubViewController *mySubVC = [[MySubViewController alloc] init];
[mySubVC setDelegate:self];
[mySubVC doSomeTask]; // this works fine
}
In these 2 methods you have created 2 separate variables of type MySubViewController. You have only set the delegate on the variable created inside viewWillAppear.
These are not static classes, singletons or anything else similar. If you create a variable of one type it is in no way linked to the other. The same way you can create multiple NSString's that have no bearing on each other.
Create it like so:
#implementation MyViewController
{
MySubViewController *_subVC;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_subVC = [[MySubViewController alloc] init];
}
now you can use _subVC everywhere in that file as the one reference

How to add SKSpriteNode from a method into another class?

I'm trying to add a SKSpriteNode one class to another class MainMenu which is a subclass of SKScene. The class I created DumpTruckFramework that defines these methods is a subclass of SKSpriteNode (Which may be the problem?). Below is all the code I am using:
#DumpTruckFramework.h
#import <SpriteKit/SpriteKit.h>
#interface DumpTruckFramework : SKSpriteNode
-(void)createDumpTruckMainBody;
#end
#DumpTruckFramework.m
#import "DumpTruckFramework.h"
#implementation DumpTruckFramework
-(SKNode*)setupDumpTruckMainBody{
SKSpriteNode *mainBody = [SKSpriteNode spriteNodeWithImageNamed:#"Dump_Truck_Main_Body.png"];
mainBody.name = #"";
mainBody.scale = 0.5;
mainBody.position = CGPointMake(300, 400);
return mainBody;
}
-(void)createDumpTruckMainBody{
SKNode *mainBody = [self setupDumpTruckMainBody];
[self addChild:mainBody];
}
#end
Above is the method I use in the MainMenu class to add the mainBody sprite. Maybe the issue could be the [self addChild:mainBody]; code line as self is referring to the DumpTruckFramework class?
#MainMenu.h
#import <SpriteKit/SpriteKit.h>
#interface MainMenu : SKScene
#end
#MainMenu.m
#import "MainMenu.h"
#import "DumpTruckFramework.h"
#implementation MainMenu
-(id)initWithSize:(CGSize)size{
if (self = [super initWithSize:size]){
//[self createMainMenuTitle];
DumpTruckFramework *dumpTruck = [[DumpTruckFramework alloc] init];
[dumpTruck createDumpTruckMainBody];
}
return self;
}
Now what I think is happening is that the method does work but doesn't do what is expected. As the MainMenu scene is what is meant to be displaying the nodes, it's declared to be the "view-able scene".
Hopefully someone can help me resolve this issue, cheers.
You're not adding a sprite node to your scene. You can change createDumpTruckMainBody to:
-(SKNode*)createDumpTruckMainBody{
SKNode *mainBody = [self setupDumpTruckMainBody];
return mainBody;
}
and in your main menu change your code to:
-(id)initWithSize:(CGSize)size{
if (self = [super initWithSize:size]){
//[self createMainMenuTitle];
DumpTruckFramework *dumpTruck = [[DumpTruckFramework alloc] init];
SKNode *node = [dumpTruck createDumpTruckMainBody];
[self addChild: node];
}
return self;
}
Hope this helps.
You can add the dumpTruck to MainScene: [self addChild: dumpTruck];.
What's happening is you are creating an instance of DumpTruckFramework which almost immediately goes out-of-scope, and is therefore destroyed:
-(id)initWithSize:(CGSize)size{
if (self = [super initWithSize:size]){
DumpTruckFramework *dumpTruck = [[DumpTruckFramework alloc] init];
[dumpTruck createDumpTruckMainBody];
// dumpTruck destroyed here
}
return self;
}
So your issue is one of scope. Looks like you need to use an instance variable to maintain the DumpTruckFramework.

Weird Error "No known class method for selector 'sceneWithSize:'"

I've recently started working with Sprite Kit now i've just opened a new sprite kit project and removed the MyScene class as I don't need it. I've then created my own subclass of SKView called PhyscisScene now when i've done this and replaced the code in ViewController.mI get this error on line 24 in ViewController.m: No known class method for selector 'sceneWithSize:' Below is all my Class files i've changed hopefully you can tell me whats going on:
ViewController.m
#import "ViewController.h"
#import "PhysicsScene.h"
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
SKScene * scene = [PhysicsScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return UIInterfaceOrientationMaskAllButUpsideDown;
} else {
return UIInterfaceOrientationMaskAll;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#end
PhysicsScene.m
#import "PhysicsScene.h"
#implementation PhysicsScene
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
PhysicsScene is a subclass of SKView. It looks like PhysicsScene should be a subclass of SKScene. The solution is to convert PhysicsScene to a subclass of SKScene since that is what it looks like you want to use it for.
Looking at the docs, sceneWithSize is a class method of SKScene, not SKView.

How can i move from a SKScene to an UIViewController

i've got a little problem with my spritkit game.
I have created a little Jump N Run game where a penguin must dodge as icebergs or you can also collect fish. As a penguin you have three lives. Are these lives now in the end because you were already met three times of icebergs, I want that one will be automatically redirected to a new UIViewController by then finally can perform new actions. I have everything created with xib files and now just do not know how I made ​​the skscene back to a UIViewController gelange ...
Would be great if someone could help me. :)
The most clean solution is to declare and implement a protocol to let the UIViewController know that it should react to a change in SKScene.
#protocol MySceneDelegate <NSObject>
- (void)closeScene;
#end
#interface MyScene : SKScene
#property(nonatomic, strong) SKShapeNode *ball;
#property (weak) id <MySceneDelegate> delegate;
#end
View controller that shows the scene should implement a closeScene method and set itself as a delegate of the scene. Example:
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
MyScene * scene = [MyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Set the delegate
[scene setDelegate:self];
// Present the scene.
[skView presentScene:scene];
}
Then in the scene you can call the closeScene method of the view controller which was set as a delegate:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
/* Called when a touch begins */
if ([_delegate respondsToSelector:#selector(closeScene)])
{
[_delegate performSelector:#selector(closeScene)];
}
}
Hope it helps.

Resources