I have a GameViewController, which transitions between different scenes, for example: CircleScene to SquareScene, back & forth with Transitions.
All of the above logic works fine, as long as GameViewController is "initial view controller".
Once I have one other view controller, above the GameViewController, the screen transitions for GameViewController doesn't work anymore.
GameViewController.swift
import UIKit
import SpriteKit
import GameplayKit
protocol ScreenManager {
func gotoSquare()
func gotoCircle()
}
class GameViewController: UIViewController, ScreenManager {
let sceneHeight = 380
let sceneWidth = 570
func gotoCircle() {
let transition = SKTransition.push(with: .right, duration: 1.0)
let newScene = CircleScene(size: CGSize(width: sceneWidth, height: sceneHeight))
newScene.screenManager = self
newScene.scaleMode = .aspectFill
let view = self.view as! SKView?
view?.presentScene(newScene, transition: transition)
}
func gotoSquare() {
let transition = SKTransition.push(with: .left, duration: 1.0)
let newScene = SquareScene(size: CGSize(width: sceneWidth, height: sceneHeight))
newScene.screenManager = self
newScene.scaleMode = .aspectFill
let view = self.view as! SKView?
view?.presentScene(newScene, transition: transition)
}
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
let scene = CircleScene(size: CGSize(width: sceneWidth, height: sceneHeight))
scene.screenManager = self
// 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
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .landscape
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
}
MainViewContoller.swift
import UIKit
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnPressed(_ sender: AnyObject) {
// Go to GameViewController
let gameViewController:GameViewController = storyboard?.instantiateViewController(withIdentifier: "GameViewControllerID") as! GameViewController
self.present(gameViewController, animated:false)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .landscape
} else {
return .all
}
}
override var prefersStatusBarHidden: Bool {
return true
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
I have tried ViewController methods (present/show) unsuccessfully. Is there anything wrong with ViewController presentation that causes scene transitions to fail? Appreciate your help.
Related
I am trying to direct the user to a main menu file as soon my game loads (made with Swift 4 SpriteKit). However, when I try to redirect the user to this .swift file (where a label should be displayed onto the users screen) the screen is simply blank except the for the node and FPS counter.
Here's my code in the MainMenuScene.swift file I created:
import Foundation
import SpriteKit
class MainMenu: SKScene {
override func didMove(to view: SKView) {
print("In scene")
let myLabel = SKLabelNode(fontNamed: "The Bold Font")
myLabel.fontColor = SKColor.blue
myLabel.text = "My Label"
myLabel.fontSize = 50
myLabel.position = CGPoint(x: 0, y: 0)
myLabel.zPosition = 1
self.addChild(myLabel)
}
}
Here's my code in the GameViewController.swift
import UIKit
import SpriteKit
import GameplayKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "MainMenuScene") {
// 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
view.showsPhysics = true
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
}
I changed the
if let scene = SKScene(fileNamed: "GameScene")
to
if let scene = SKScene(fileNamed: "MainMenuScene")
That line of code should redirect the user to MainMenuScene.swift, but when I run the program the screen is simply blank.
Anyone know what im doing wrong?
Thanks!
Try using the code below after
super.viewDidLoad()
Also make sure that your Menu class is called "MainMenuScene" and that you changed the scene name to the same at MainMenuScene.sks
CODE:
if let scene = GKScene(fileNamed: "MainMenuScene") {
// Get the SKScene from the loaded GKScene
if let sceneNode = scene.rootNode as! MainMenuScene? {
scene.scaleMode = .aspectFill
// Present the scene
if let view = self.view as! SKView? {
view.presentScene(sceneNode)
view.ignoresSiblingOrder = true
//view.showsFPS = true
//view.showsNodeCount = true
}
}
}
I am working on SpriteKit Game. From within GameScene i want to represent another viewController. I have done it correctly, but when i dismiss Modally presented viewController, GameScene on my Root viewController freezes. Here is my code on GameScene....
NotificationCenter.default.post(name: NSNotification.Name(rawValue:"present"), object:nil)
Here is my code for root viewController(GameViewController)
override func viewDidLoad() {
super.viewDidLoad()
let gameScene = GameScene()
let skView = self.view as! SKView
skView.showsFPS = true
skView.ignoresSiblingOrder = true
let size = CGSize(width:590, height:390)
/* Set the scale mode to scale to fit the window */
//menuScene.scaleMode = .aspectFit
// menuScene.anchorPoint = CGPoint(x:0, y:0)
//skView.allowsTransparency = true
//size our scene to fit the view exactly:
// let rect1 = CGRect(origin:point, size:size)
//skView.setNeedsUpdateConstraints()
gameScene.size = CGSize(width:size.width, height:size.height)
gameScene.scaleMode = .aspectFill
skView.presentScene(gameScene)
skView.translatesAutoresizingMaskIntoConstraints = false
skView.ignoresSiblingOrder = true
skView.showsFPS = true
skView.showsNodeCount = true
NotificationCenter.default.addObserver(self, selector: #selector(self.presentVC), name: NSNotification.Name(rawValue:"present"), object: nil)
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
func presentVC(){
let toViewController = viewController() as UIViewController
toViewController.modalPresentationStyle = .overFullScreen
self.present(toViewController, animated: true, completion: nil)
toViewController.transitioningDelegate = self
print("presenting next")
}
And Here is the code for another viewController that is to be represented modally over rootViewController:
override func viewDidLoad() {
super.viewDidLoad()
let imageView = UIImageView(image:UIImage(named:"Background1"))
imageView.frame = CGRect(x:0, y:0, width:1000, height:800)
let aButton = UIButton()
aButton.frame = CGRect(x:view.frame.width/2 - 50, y:view.frame.height/2 - 50, width:100, height:100)
aButton.setTitle("aButton", for: .normal)
aButton.backgroundColor = .green
aButton.addTarget(self, action: #selector(pressed), for: .touchUpInside)
aButton.isEnabled = true
view.addSubview(imageView)
view.addSubview(aButton)
}
func pressed(){
dismissVC()}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
func dismissVC(){
self.dismiss(animated: true, completion: nil)
print("returning previous")
}
Here is the answer with explanation. First of all it was not GameScene in frozen state. But it was view of another viewController that was present even after completion of activity on modally presented viewController. Presence of view of another viewController was due to nonExecution of viewController dismiss code inside that viewController. So presence of another viewController view was not allowing me to interact with my app. Now Solution is, In above Posted question, In GameViewController code i have added presentVC() function. Inside that instead of line:
self.present(toViewController, animated: true, completion: nil)
Use following line:
self.show(toViewController, sender:nil)
Explanation
I have a game that runs in landscape, and I'm trying to send an email, also, in portrait mode.
Basically:
if the actual view is GameViewController = .Landscape;
if the actual view is MailViewController = .AllButUpsideDown.
Demo code
I built this code below, but I'm having an issue with it:
Mail isn't being called when touching the screen.
You can download this code here.
GameScene
import SpriteKit
class GameScene: SKScene {
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
//Mail
NSNotificationCenter.defaultCenter().postNotificationName("openMail", object: nil)
}
}
GameViewController
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Set view size.
let scene = GameScene(size: view.bounds.size)
// Configure the view.
let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .ResizeFill
skView.presentScene(scene)
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .Landscape
} else {
return .Landscape
}
}
}
MailViewController
import UIKit
import SpriteKit
import MessageUI
class MailViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Register mail observer (so I can call from GameScene)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MailViewController.openMailController), name: "openMail", object: nil)
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .AllButUpsideDown
} else {
return .All
}
}
}
//Mail
extension MailViewController: MFMailComposeViewControllerDelegate {
func openMailController() {
let mailComposerVC = MFMailComposeViewController()
mailComposerVC.mailComposeDelegate = self //extremely important to set the mailComposeDelegate property, not the delegate property
self.presentViewController(mailComposerVC, animated: true, completion: nil)
}
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Thanks for any help,
Luiz.
Problem with your provided code is that observer in MailViewController class view did load method is not called. So Observer is not set to receive any notification.
Also you didn't called segue anywhere in your code to call MailViewController.
Solution: By using Delegate Method
GameScene class updated:
import SpriteKit
// Protocol
protocol gameSceneDelegate {
func openMail()
}
// Protocol
class GameScene: SKScene {
// Delegate
var sceneDelegate : gameSceneDelegate?
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
//Mail
// NSNotificationCenter.defaultCenter().postNotificationName("openMail", object: nil)
// delegate method called on receiving touches
self.sceneDelegate?.openMail()
}
}
GameViewController class updated:
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Set view size.
let scene = GameScene(size: view.bounds.size)
// Delegate Confirmation
scene.sceneDelegate = self
// Configure the view.
let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .ResizeFill
skView.presentScene(scene)
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .Landscape
} else {
return .Landscape
}
}
}
// Delegate Method Implementation
extension GameViewController : gameSceneDelegate{
func openMail(){
self.performSegueWithIdentifier("openMail", sender: self);
}
}
Mail View Controller:
import UIKit
import SpriteKit
import MessageUI
class MailViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Register mail observer (so I can call from GameScene)
// NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MailViewController.openMailController), name: "openMail", object: nil)
self.openMailController()
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .AllButUpsideDown
} else {
return .All
}
}
}
//Mail
extension MailViewController: MFMailComposeViewControllerDelegate {
func openMailController() {
let mailComposerVC = MFMailComposeViewController()
mailComposerVC.mailComposeDelegate = self //extremely important to set the mailComposeDelegate property, not the delegate property
self.presentViewController(mailComposerVC, animated: true, completion: nil)
}
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Last change in storyboard:
click segue connecting GameViewController and MailViewController and change identifier to - openMail or whatever you want to keep
I currently have a bug with the app I am working on where when a user taps on an iAd banner and the ad loads, the game resets. By "resets" I mean all their progression (other than the NSUserDefaults data) is reset to 0 and the scene is reset back to the start of the game.
My game primary runs from didMoveToView() and touchesBegan(). In my GameViewController the game scene is built from viewWillLayoutSubviews().
Here is how I control my ads and placement. Not sure if has to do with the placement of my code or the ad.
import UIKit
import SpriteKit
import iAd
// Global Ad Variable
var adBanner: ADBannerView!
class GameViewController: UIViewController, ADBannerViewDelegate {
/* Load Ads */
func loadAds() {
adBanner = ADBannerView(frame: CGRect(x: 0, y: view.bounds.size.height - 50, width: 320, height: 50))
adBanner.delegate = self
adBanner.hidden = true
self.view.addSubview(adBanner)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
// Create and Configure the scene
scene.size = skView.bounds.size
skView.showsFPS = false // SHOW OR HIDE FRAMES PER SECOND
skView.showsNodeCount = false
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
}
override func awakeFromNib() {
super.awakeFromNib()
loadAds()
}
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.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
adBanner.hidden = true
print("There was an error loading ad")
}
func bannerViewWillLoadAd(banner: ADBannerView!) {
print("Ad Loading")
}
func bannerViewDidLoadAd(banner: ADBannerView!) {
adBanner.hidden = false
print("Ad Loaded")
}
}
EDIT: OK, so this happens even with an empty SpriteKit scene!
What could be wrong here??
Why does iOS need 2 seconds to display SpriteKit scene??
EDIT 2: First time i click on tab bar to display view controller with SKScene it displays immediately, but every next time i try to navigate back to this view controller it will take it 2 seconds to display!
I have a a tab bar controller in my app with multiple different viewControllers.
One of them contains a SpriteKit scene, with a couple of nodes, 5 of them, nothing too heavy.
In simulator everything is fine, but when testing on the device i noticed that sometime there is a big halt ( around 2 seconds! ) when changing to ViewController with the SpriteKit scene.
Other times there is no halt and view is displayed immediately.
Now, i know there must be something I'm doing wrong here, because iOS should definitely be able to handle this.
This is my viewDidLoad function inside the viewController with the spriteKit scene:
override func viewDidLoad() {
super.viewDidLoad()
if let scene = MyScene(fileNamed:"MyScene") {
// Configure the view.
scene.switchView = switchView
let parentNode = scene.childNodeWithName("WholeObject") as! SKSpriteNode
let contentNode = scene.childNodeWithName("CenterNode") as! SKSpriteNode
addPhotoToFrame(contentNode, photoName: "woman", maskName: "circleMask")
let node1 = parentNode.childNodeWithName("node1") as! SKSpriteNode
addPhotoToFrame(zealNode1, photoName: "motherCircleImage", maskName: "circleMaskSmall")
let node2 = parentNode.childNodeWithName("node2") as! SKSpriteNode
addPhotoToFrame(zealNode2, photoName: "hairstylistCircleImage", maskName: "circleMaskSmall")
let node3 = parentNode.childNodeWithName("node3") as! SKSpriteNode
addPhotoToFrame(zealNode3, photoName: "dietCircleImage", maskName: "circleMaskSmall")
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
}
hi deloki i created a new project in swift and come with a solution its working fine on device..... check out my code
here is my GameViewController which call GameScene
import UIKit
import SpriteKit
extension SKNode {
class func unarchiveFromFile1(file : String) -> SKNode? {
if let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks") {
var sceneData = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe, error: nil)!
var archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as! GameScene
archiver.finishDecoding()
return scene
} else {
return nil
}
}
}
class GameViewController: UIViewController {
var skView:SKView!
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene.unarchiveFromFile1("GameScene") as? GameScene {
// Configure the view.
let graphRect:CGRect = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height)
skView = SKView();
skView.frame=graphRect
skView.showsFPS = true
skView.showsNodeCount = true
self.view.addSubview(skView)
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
skView.presentScene(scene)
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(3 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue()) {
let s2=GameViewController1();
self.presentViewController(s2, animated: true, completion: { () -> Void in
//
})
}
}
}
override func viewDidDisappear(animated: Bool) {
if((skView) != nil)
{
skView .removeFromSuperview();
skView=nil;
}
}
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> Int {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return Int(UIInterfaceOrientationMask.AllButUpsideDown.rawValue)
} else {
return Int(UIInterfaceOrientationMask.All.rawValue)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
here is my GameViewController1 which call MyScene1
import UIKit
import SpriteKit
extension SKNode {
class func unarchiveFromFile(file : String) -> SKNode? {
if let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks") {
var sceneData = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe, error: nil)!
var archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as! GameScene
archiver.finishDecoding()
return scene
} else {
return nil
}
}
}
class GameViewController1: UIViewController {
var skView:SKView!
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene.unarchiveFromFile("MyScene1") as? GameScene {
// Configure the view.
let graphRect:CGRect = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height)
skView=SKView()
skView.frame=graphRect
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
// scene.scaleMode = .AspectFill
self.view.addSubview(skView)
skView.presentScene(scene)
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(3 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue()) {
let s2=GameViewController();
self.presentViewController(s2, animated: true, completion: { () -> Void in
//
})
}
}
}
override func viewDidDisappear(animated: Bool) {
if((skView) != nil)
{
skView .removeFromSuperview();
skView=nil;
}
}
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> Int {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return Int(UIInterfaceOrientationMask.AllButUpsideDown.rawValue)
} else {
return Int(UIInterfaceOrientationMask.All.rawValue)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
creating SKview from storyboard i just created it from programmitically and every 2 second i am switching from one view to another and its working fine on all device but one thing i used Xcode 6.4 and ios9
you can download link from http://www.filedropper.com/tryit
I have no idea what triggers the issue but pausing before the view disappeared fixed it for me.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.contentView.paused = false
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.contentView.paused = true
}