Terminated due to memory issue - ViewControllers taking up memory - swift

I'm developing a game using sprite kit and have various view controllers for different scenes. Like story mode, multiplayer etc...
I've noticed a problem where every time I open up a view controller with a SpriteKit scene, the memory usage for my app increases. And when the view is dismissed, that memory isn't allocated back?
Is there something I need to be doing in my code to return the memory the ViewController or SpriteKit scene used up upon loading?
EDIT: As requested, here is some code from when the VC loads up with a SpriteKit scene & the code used to dismiss the VC:
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
if let scene = SKScene(fileNamed: "GameScene.sks") {
scene.scaleMode = .aspectFill
view.presentScene(scene)
(scene as! GameScene).parentVC = self
}
}
}
For the view dismissal, I first present a loading screen over the UIApplication.shared.keyWindow and then dismiss:
dismiss(animated: true) {
LoadingScreen?.removeFromSuperview()
}
The loading screen class is handled in the AppDelegate.

I think it's an issue of retain cycles
1 if let view = self.view as! SKView? {
2 if let scene = SKScene(fileNamed: "GameScene.sks") {
3 scene.scaleMode = .aspectFill
4 view.presentScene(scene)
5 (scene as! GameScene).parentVC = self
}
}
if line 5, inside the closure, you're referencing self, so when the viewcontroller should be deallocated, the reference on line 5 is stopping the deallocation, Can you try this :
if let view = self.view as! SKView? {
if let scene = SKScene(fileNamed: "GameScene.sks") {
scene.scaleMode = .aspectFill
view.presentScene(scene)
guard let scene = scene as? GameScene else { return }
guard let scencePVC = scene.parentVC else { return }
scencePVC = self
}
}
I'm trying to fix the issue without knowing the object hierarchy but it's hard. Basically the issue is that you have 2 objects( viewcontrollers) referencing each other that would stop the deallocation.
You can read more about this issue here & here

Related

SpriteKit scene deformed after modal presentation swift

Could use some help troubleshooting an issue with a SpriteKit scene.
I have a scene that displays some coins in the main section of the app.
When I present a viewcontroller from the bottom I have no issue. Same for tab bar navigation, no issues.
Here is the view as it should always be displayed.
The issue comes only when I present a viewcontroller from the side.
When the new viewcontroller is dismissed, the scene works, but is distorted.
this is how it is displayed after a viewcontroller is displayed modally and later on dismissed.
EDIT: I forgot to mention that if I swipe vertically on the distorted scene, the distortion is fixed and all is good.
Here is some of the code in viewDidAppear of the viewcontroller.
Thanks for the help.
EDIT 2:
I just tested the app on a iPhone 5 using iOS 10 and the issue doesn't happen. Any chance this might be iOS 11 related?
func configureScene(_ completion: () -> Void) {
defer { completion() }
guard wScene == nil else { return }
let skView = SKView(frame: self.view.frame)
skView.isUserInteractionEnabled = false
skView.backgroundColor = .clear
wScene = WScene(size: view.frame.size)
wScene.backgroundColor = .clear
skView.presentScene(wScene)
view.insertSubview(skView, belowSubview: collectionView)
if let buttonsObstacle = doubleButton?.buttonsView {
let obstacleSize = CGSize.init(width: buttonsObstacle.frame.width, height: buttonsObstacle.frame.height)
obstacle = SKSpriteNode.init(color: .clear, size: obstacleSize)
guard let obstacle = obstacle else { return }
obstacle.name = WScene.obstacleNodeName
let convertedOrigin = view.convert(buttonsObstacle.center, from: buttonsObstacle.superview)
let skConvertedOrigin = skView.convert(convertedOrigin, to: wScene)
obstacle.position = skConvertedOrigin
obstacle.physicsBody = SKPhysicsBody(rectangleOf: obstacleSize)
obstacle.physicsBody?.allowsRotation = false
obstacle.physicsBody?.isDynamic = false
source.scrollHandler = { [weak self] (scrollView) in
guard let strongSelf = self else { return }
strongSelf.buttonsMoved(inView: skView, withScroll: scrollView)
}
wScene.addChild(obstacle)
presenter.loadData()
}
}
I solved my issue.
It was related to the new iOS 11 adjustedContentInset property.
My Coin SK scene was being moved by the scroll handler when the view appeared after a modal transition.
My solution is to disable the scrolling for the first 0.1 second after the view appears. In this way iOS 11 doesn't touch the coins anymore while users are able to scroll correctly because they interact with the view most of the time after at least 0.1 seconds.

Changing the initial game scene in Swift

I want to change the initial scene being presented to be another class other than the default GameScene class. From reading other questions, I understand I must change this part from the GameViewController:
if let scene = SKScene(fileNamed: "GameScene") {
print(scene)
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}
So within the GameScene.swift file I am creating a new class:
class MainMenu : SKScene {
override func didMove(to view: SKView) {
print("At least it ran")
self.scene?.view?.presentScene(GameScene())
}
}
However, when I change the scene to:
if let scene = SKScene(fileNamed: "MainMenu")
When I run the project, it gets stuck, but when I run it with the string "GameScene" then it works perfectly. I am doing something wrong loading the MainMenu?
In my opinion you should separate your scenes into their own files...
Do you have an corresponding SKS file for MenuScene? You need to create one if you are trying to load it with fileNamed:
or -
Use this code to load a SKScene file that is created in code only and not in the Scene editor
if let skView = self.view as? SKView {
if skView.scene == nil {
let scene = MenuScene(size: skView.bounds.size)
scene.scaleMode = .aspectFill
skView.presentScene(scene)
}
}
and then in your MenuScene file you will need an init func
init(size: CGSize) {
super.init(size: size)
name = "menuScene"
}

Preload Texture Atlases Completion Handler not working

I have a UIImageView for the user to look at (looks almost like a splash screen) come up so that I can preload my texture atlases.
But it seems that in the completion block, even though I say to 'removefromsuperview' it doesn't do so.
I know that the textures have finished loading because if I tap the screen the UIImageView goes away and my game starts, but it won't go away on its own for some odd reason.
Here is my viewDidLoad inside my GameViewController:
//Configure the View
let skView = view as! SKView
skView.multipleTouchEnabled = false
self.view.exclusiveTouch = false
//Create and Configure the scene
scene = GameScene(size: skView.bounds.size)
scene.scaleMode = .AspectFill
//Load "SPLASH SCREEN"
let image: UIImage = UIImage(named: "ViewControllerImage")!
GameViewController.splashView = UIImageView(image: image)
GameViewController.splashView!.frame = CGRectMake(0,0,self.view.bounds.width,self.view.bounds.height)
self.view.addSubview(GameViewController.splashView!)
SKTextureAtlas.preloadTextureAtlases([GameViewController.diploTextureAtlas,GameViewController.bieberTextureAtlas,GameViewController.skrillexTextureAtlas,GameViewController.sunTextureAtlas], withCompletionHandler: { () -> Void in
print("Completo")
GameViewController.splashView?.removeFromSuperview()
skView.presentScene(self.scene)
})
Try this code:
//Configure the View
let skView = view as! SKView
skView.multipleTouchEnabled = false
self.view.exclusiveTouch = false
//Create and Configure the scene
scene = GameScene(size: skView.bounds.size)
scene.scaleMode = .AspectFill
//Load "SPLASH SCREEN"
let image: UIImage = UIImage(named: "ViewControllerImage")!
GameViewController.splashView = UIImageView(image: image)
GameViewController.splashView.tag = 666
GameViewController.splashView!.frame = CGRectMake(0,0,self.view.bounds.width,self.view.bounds.height)
self.view.addSubview(GameViewController.splashView!)
SKTextureAtlas.preloadTextureAtlases([GameViewController.diploTextureAtlas,GameViewController.bieberTextureAtlas,GameViewController.skrillexTextureAtlas,GameViewController.sunTextureAtlas], withCompletionHandler: { () -> Void in
print("Completo")
for view in self.view!.subviews {
if view.tag == 666 {
view.removeFromSuperview()
}
}
skView.presentScene(self.scene)
})
You can change for cycle also with:
if let viewWithTag = self.view.viewWithTag(666) {
viewWithTag.removeFromSuperview()
}

multiple sks files, calling proper scene based on GVC in SpriteKit

I have five iPad scenes, and 5 iPhone Scenes in my GVC I have this, I can call the Welcome Scene and Hud Scene no problem, yet I can not replicate the same calls for the other scenes. I do not know if it is the curly braces, yet I can not seem to call the other iPad scenes it defaults to the iPhone scene. (trust me I was pretty excited when the HUD, showed up.) Can someone give some advice on the structure of my code. Please....
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if (UIDevice.currentDevice().userInterfaceIdiom == .Phone) {
if let scene = WelcomeScene(fileNamed:"WelcomeScene") {
let skView = self.view as! SKView
skView.showsFPS = GlobalData.Constant.DEV.DEBUG
skView.showsNodeCount = GlobalData.Constant.DEV.DEBUG
skView.ignoresSiblingOrder = true
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
} else {
if let scene = WelcomeScenePad(fileNamed: "WelcomeScenePad") {
let skView = self.view as! SKView
skView.showsFPS = GlobalData.Constant.DEV.DEBUG
skView.showsNodeCount = GlobalData.Constant.DEV.DEBUG
skView.ignoresSiblingOrder = true
scene.scaleMode = .AspectFill
skView.presentScene(scene)
if let scene = HudScenePad(fileNamed: "HudScenePad") {
let skView = self.view as! SKView
skView.showsNodeCount = GlobalData.Constant.DEV.DEBUG
skView.ignoresSiblingOrder = true
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
} else if let scene = WelcomeScene(fileNamed:"WelcomeScene") {
let skView = self.view as! SKView
skView.showsFPS = GlobalData.Constant.DEV.DEBUG
skView.showsNodeCount = GlobalData.Constant.DEV.DEBUG
skView.ignoresSiblingOrder = true
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
}
}
override func prefersStatusBarHidden() -> Bool {
return false
}
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .AllButUpsideDown
} else {
return .All
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
}
This bit of code lets me know that the game scene is going to proper sks file, and prints the value in the editor, to verify, this is in my gameOver Function.
if (UIDevice.currentDevice().userInterfaceIdiom == .Pad ){
if let _ = SceneTypePad(rawValue: fullSKSNameToLoad + String("Pad")) {
fullSKSNameToLoad = "fileNamed" + "Pad"
goToScenePad(SceneTypePad.DebateScene1Pad)
print(("Went to DebateScenePad"))
_ = SKTransition.fadeWithDuration(3)
_ = DebateScene1Pad()
}
} else if (UIDevice.currentDevice().userInterfaceIdiom == .Phone ){
goToScene(SceneType.DebateScene1)
(print("Went to DebateScenePhone"))
_ = SKTransition.fadeWithDuration(3)
_ = DebateScene1()
}
Three secs is too long to wait btw... :)
I needed to create a new static variable to call the SKs pad scene in my GlobalData/Base Scene file. Noob mistake. Word to the wise when duplicating code for the new files double check the variables, when duplicated swift files. Also, it is good to give the mind a break, individuals can not keep their attention span and be sharp after 14 hours of coding.... Take a walk, sort it out with a fresh mind. Deadlines are crucial in this business, yet sometimes pressure can bring about silly decisions that can be easily solved.
static var previousScenePad:SceneTypePad?
static var nextScenePad:SceneTypePad?
if (UIDevice.currentDevice().userInterfaceIdiom == .Phone) {
if (UIDevice.currentDevice().userInterfaceIdiom == .Pad ){
if let _ = SceneTypePad(rawValue: fullSKSNameToLoad + String("Pad")) {
fullSKSNameToLoad = "fileNamed" + "Pad"
}
} else if (UIDevice.currentDevice().userInterfaceIdiom == .Phone ){
}
Does work in GVC when calling particular SKS files. Obviously it renders the proper view based upon using the correct fileName "" that is defined in the base class, yes I am aware, that there are better way to use one SKS files, and using camera nodes to adjust views. When it comes to content, and having more space it gives opportunity to create a different experience with more pixels, and more space. Assets are equally important, as in the beginning I was calling the wrong assets per device, I decided to hard code, the scale factors for my look without losing resolution. The end result will be a universal app that works cross platforms. If you optimize right, I was able to condense a 140mb app, to 76mb, even though I added 5 new scenes to my app bundle. Check your background music, check your sound effects, and PNG files. Limit emitters to the effect that looks good. You can always go back and find cleaner approach in the next update!!!

SpriteKit Scene order

I'm creating a game using Sprite Kit. I have my GameScene and GameOverScenenow I want to create a MenuScene Where do I control the order of the MenuScene so that it shows up after LaunchScene
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene.unarchiveFromFile("MenuScene") as? GameScene {
// Configure the view.
let skView = self.view as! SKView
skView.ignoresSiblingOrder = true
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
}
Edit tried putting ("MenuScene") as the scene but when I run the game that scene won't show
Ok so I found out that I was one the right path. I just needed to change this line. Instead of this
if let scene = GameScene.unarchiveFromFile("MenuScene") as? GameScene {
add this
if let scene = MenuScene.unarchiveFromFile("MenuScene") as? MenuScene {