SKScene Won't Present When SubView Is Added To View Controller - swift

I tried to add a subview to my GameViewController (because I want to add a banner ad at the bottom). I created it as follows:
if let view = self.view as! SKView? {
let subView = SKView(frame: CGRect(origin: CGPoint.zero, size: CGSize(width: 415, height: 685)))
view.addSubview(subView)
if let scene = SKScene(fileNamed: "GameScene") {
scene.scaleMode = SKSceneScaleMode.aspectFit
subView.presentScene(scene)
view.ignoresSiblingOrder = true
view.showsFPS = false
view.showsNodeCount = false
view.showsDrawCount = false
addBannerViewToView(bannerView)
}
}
This works initially - it presents my GameScene. But later, I ask my GameScene to present another subclass of SKScene that I created, which I call Shop, using the following code:
func initializeShop() {
let shop = Shop(size: self.size)
let transition = SKTransition.fade(withDuration: 0.1)
self.view?.presentScene(shop!, transition: transition)
}
And it simply does not present. It remains on the current view.
This works completely fine if I do NOT add a subview to the initial view in the GameViewController, i.e.
if let view = self.view as! SKView? {
if let scene = SKScene(fileNamed: "GameScene") {
scene.scaleMode = SKSceneScaleMode.aspectFit
view.presentScene(scene)
view.ignoresSiblingOrder = true
view.showsFPS = false
view.showsNodeCount = false
view.showsDrawCount = false
}
}
So it seems like there is some property of the GameViewController's default SKView that allows presentation, but not a subview. But I can't find any documentation that says that - from what I can tell, all SKViews should be able to present SKScenes.
Any help would be much appreciated.

Related

Re-position scene to top of view

Apple's documentation states I can't change the position using the position getter/setter. I've tried the anchor point suggestion but still unable to move my scene's position in Scene Editor to the top. Does anyone have any ideas?
Implementation has been done via Scene Editor, currently the only code submitted to help scene present is below:
import UIKit
import SpriteKit
import GameplayKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Load 'GameScene.sks' as a GKScene. This provides gameplay related content
// including entities and graphs.
if let scene = GKScene(fileNamed: "GameScene") {
// Get the SKScene from the loaded GKScene
if let sceneNode = scene.rootNode as! GameScene? {
// Copy gameplay related content over to the scene
sceneNode.entities = scene.entities
sceneNode.graphs = scene.graphs
// Set the scale mode to scale to fit the window
sceneNode.scaleMode = .aspectFit
print(sceneNode.anchorPoint)
print(sceneNode.position)
// Present the scene
if let view = self.view as! SKView? {
view.presentScene(sceneNode)
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
}
}
In AppDelegate bypass storyboard.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
let viewController = GameViewController()
window?.rootViewController = viewController
window?.makeKeyAndVisible()
return true
}
In GameViewController
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(skView)
setupSKView()
}
let skView: SKView = {
let view = SKView()
if let scene = SKScene(fileNamed: "GameScene") {
scene.scaleMode = .fill
view.presentScene(scene)
}
return view
}()
func setupSKView(){
skView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height / 2)
}
}

Why does getting my view's frame return the full screen, even tho the view has been run through the safeArea constraints?

Before presenting my GameScene I apply the safeAreaLayoutGuide to the initial view screen (code below is from GameViewController).
If at any point before I call view.presentScene(scene) I try to get the view's frame, I get back an empty set.
If however I get the frame, after presenting the scene, I get the rect for the full screen, even though the view does appear within the safeAreaLayoutGuide.
How can I get the view's safe adjusted screen coordinates? I should note I am doing this so I can apply the proper constraints to my GameScene, as I can only find how to do it for UIView.
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView?{
let screenWidthInPoints = UIScreen.main.bounds.width
let screenHeightInPoints = UIScreen.main.bounds.height
let imageView = UIView()
imageView.backgroundColor = .red
view.addSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
imageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
imageView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor , constant: -0).isActive = true
let scene = GameScene(size: CGSize(width: screenWidthInPoints,
height: screenHeightInPoints))
scene.backgroundColor = .black
scene.scaleMode = .aspectFit
view.presentScene(scene)
var myRect = view.frame
UIScreen.main.bounds represents the entire screen, not just the safe area. You will have to create a separate view constrained to safe area insets to get the proper safe area size. In your case, you already have an imageView placed within the safe area. Try printing out its size after adding the constraints and do it within a DispatchQueue.main.async to make sure that it gets called only after the current pass layout is complete.
let imageView = UIView()
imageView.backgroundColor = .red
view.addSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
imageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
imageView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor , constant: -0).isActive = true
DispatchQueue.main.async {
print("imageView.frame: ", imageView.frame)
let scene = GameScene(size: CGSize(width: imageView.frame.width,
height: imageView.frame.height))
scene.backgroundColor = .black
scene.scaleMode = .aspectFit
view.presentScene(scene)
}

How do I add a sprite to a viewcontroller?

I have made my Viewcontroller to a SKview so I can add things.
let skview: SKView = {
let view = SKView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
self.view.addSubview(skview)
skview.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
skview.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
skview.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
skview.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
self.addchild(player) doesn't work.
You can't add a sprite to a view controller. You can't neither add a sprite to a SKView. You must first create a SKScene and present it:
let scene = SKScene(size: skview.bounds.size)
skview.presentScene(scene)
Then you can add sprites to the scene:
scene.addChild(player)

Multiple scenes in macOS Spritekit Game

I am working on a game for macOS using Spritekit. Want to use a second scene. How do I set up App Delegate and ViewController to accomplish that?
I can make a transition to a second scene
var secondScene = SecondScene(size: self.size)
var transition = SKTransition.flipVertical(withDuration: 1.0)
secondScene.scaleMode = SKSceneScaleMode.aspectFill
self.scene!.view?.presentScene(secondScene, transition: transition)
Similar code in the SecondScene to return results in a crash during reloading of the previous scene, which loads fine the first time around.
self.scene!.view?.presentScene(FirstScene, transition: transition)
This is what my ViewController looks like.
class ViewController: NSViewController {
#IBOutlet var skView: SKView!
#IBOutlet weak var msg: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.skView {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
}

Why my iAd banner not showing on my gameView?

I just finished my first game and I'm trying this code on my viewController to integrate an ad banner :
import iAd
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var skView = self.originalContentView as SKView
var scene:SKScene = GameScene(size: skView.bounds.size)
skView.showsFPS = false
skView.showsNodeCount = false
skView.showsPhysics = false
let turbulence = SKFieldNode.turbulenceFieldWithSmoothness(0.1, animationSpeed: 0.1)
scene.addChild(turbulence)
let noise = SKFieldNode.turbulenceFieldWithSmoothness(0.4, animationSpeed: 0.1)
scene.addChild(noise)
skView.presentScene(scene)
self.canDisplayBannerAds = true
}
On my firstViewController (the game presentation) it works but not on my gameViewController and I can't understand why.
Someone can help me ?
Thank you all !!!