4

I need help presenting an alert view in the game scene. Im currently struggling to do so as GameScene.Swift isnt a standard ViewController. If it helps I need to do so as I need the user to input a value which is used as a coordinate for the ball Sprite Kit Node in my game. The input is only a standard integer so that isnt an issue. Any other idea of how I can do this which isnt through an alert view is also welcome.

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    let view = self.view as! SKView

    if view.scene == nil {

        view.showsFPS = false
        view.showsNodeCount = false

        let gameScene = GameScene(size: view.bounds.size)
        gameScene.scaleMode = SKSceneScaleMode.AspectFill
        view.presentScene(gameScene)
    }


}

That is in the GameViewController file

    var vc : GameViewController!



override init(size: CGSize) {
    super.init(size: size)

    let alertController = UIAlertController(title: "Bll Starting Position", message: "Please Enter a X Coordinate Value IN Range 0 to 345 ", preferredStyle: .Alert)
    alertController.addTextFieldWithConfigurationHandler { (textField) in
        textField.placeholder =  "Value Must Be In Range 0 To 345"
        textField.autocapitalizationType = UITextAutocapitalizationType.None
        textField.autocorrectionType = UITextAutocorrectionType.No
        textField.clearsOnBeginEditing = true
        textField.clearsOnInsertion = true
        textField.clearButtonMode = UITextFieldViewMode.Always
        let cancelBtn = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
        let confirmBtn = UIAlertAction(title: "Confirm", style: .Default, handler: { (confirmView) in
            if let field = alertController.textFields![0] as? UITextField {

            }
        })
        alertController.addAction(confirmBtn)
        alertController.addAction(cancelBtn)
        self.vc.presentViewController(alertController, animated: true, completion: nil)

    }

Thanks

2 Answers2

13

You can show UIAlertControllers directly from SKScenes, simply show them on the rootViewController, which is probably the best place to show them anyway.

view?.window?.rootViewController?.present...

In general its not the best practice to reference the GameViewController in SKScenes and I never actually got to a point where I was forced to do so. NSNotificationCenter, delegation or protocol extensions are the better way.

I actually use a helper for Alerts I made using Swift 2's protocol extensions.

Just make a new .swift file and add this code

import SpriteKit

protocol Alertable { }
extension Alertable where Self: SKScene {

    func showAlert(withTitle title: String, message: String) {

        let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)

        let okAction = UIAlertAction(title: "OK", style: .cancel) { _ in }
        alertController.addAction(okAction)

        view?.window?.rootViewController?.present(alertController, animated: true)
    }

    func showAlertWithSettings(withTitle title: String, message: String) {

        let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)

        let okAction = UIAlertAction(title: "OK", style: .cancel) { _ in }
        alertController.addAction(okAction)

        let settingsAction = UIAlertAction(title: "Settings", style: .default) { _ in

            guard let url = URL(string: UIApplicationOpenSettingsURLString) else { return }
            if #available(iOS 10.0, *) {
                UIApplication.shared.open(url)
            } else {
                UIApplication.shared.openURL(url)
            }
        }
        alertController.addAction(settingsAction)

        view?.window?.rootViewController?.present(alertController, animated: true)
    }
}

Now in your scenes you need to show alerts you simply conform to the protocol

class GameScene: SKScene, Alertable {

} 

and call the methods like

showAlert(withTitle: "Alert title", message: "Alert message")

as if they are part of the scene itself.

Hope this helps

crashoverride777
  • 10,581
  • 2
  • 32
  • 56
4

There may be the following options:

1) Quick solution. Do not use UIAlertController, use UIAlertView. Like that:

alert.show()

However, UIAlertView is deprecated so it's not quite safe to rely on it.

2) A better solution. Make your SKScene subclass hold a reference to the view controller which you use to present the scene and when you create the scene assign it the view controller:

myScene.viewController = self

And then you can use it.

self.viewController.presentViewController(alertController, animated: true, completion: nil)
Andrey Chernukha
  • 21,488
  • 17
  • 97
  • 161
  • Hi, Thanks for the reply. Im lost on how I would approach your 2) solution. Could you maybe explain how I do it? –  Sep 18 '16 at 12:19
  • 1
    is your scene a subclass of SKScene class? – Andrey Chernukha Sep 18 '16 at 12:21
  • 1
    Im new to SpriteKit but if what youre asking is that on the top of the game scene.swift file the class is SKScene then yes –  Sep 18 '16 at 12:27
  • 1
    does it look like "class GameScene : SKScene"? – Andrey Chernukha Sep 18 '16 at 12:28
  • Yes it does look like that –  Sep 18 '16 at 12:30
  • 1
    ok. inside GameScene class write the following: var viewController : UIViewController! – Andrey Chernukha Sep 18 '16 at 12:31
  • Ok just Done That –  Sep 18 '16 at 12:51
  • 1
    Now go to the place in your code where you create the scene. I'm pretty sure it's happening inside some view controller class (if it's not then show the code how you create the scene and tell me where it happens) and then write the following: scene.viewController = self – Andrey Chernukha Sep 18 '16 at 12:55
  • Hi yh i create the scene in the GameViewController.swift file in the viewWillLayoutSubviews. I will update the post with the code one second pleae –  Sep 18 '16 at 12:57
  • Hi updated the post with my code when creating the scene –  Sep 18 '16 at 12:59
  • 1
    so now write gameScene.viewController = self right after view.presentScene(gameScene) – Andrey Chernukha Sep 18 '16 at 13:01
  • it doesnt let me as .viewController isnt recognised. I get an error message : ''value of type 'GameScene' has no member 'viewController' –  Sep 18 '16 at 13:06
  • 1
    but it DOES have member viewController as you've written it there before, haven't you? Make sure GameScene class now has property viewController, make sure you've put it where it's supposed to be. – Andrey Chernukha Sep 18 '16 at 13:09
  • Oh i havent written it before. Where do I do that? –  Sep 18 '16 at 13:14
  • inside GameScene class write the following: var viewController : UIViewController! You told me you had done it! – Andrey Chernukha Sep 18 '16 at 13:17
  • Oh ok nevermind its working now. However when it runs the self.viewController.presentViewController(alertController, animated: true, completion: nil) it gives an error message saying -> fatal error: unexpectedly found nil while unwrapping an Optional value –  Sep 18 '16 at 13:21
  • 1
    this is because the view controller is nil. It is probably not assigned. Make sure it has been assigned – Andrey Chernukha Sep 18 '16 at 13:25
  • What do you mean by assigning. Cause I have already written the code for the alert control. Just updated the post with the code to the alert controller. –  Sep 18 '16 at 13:26
  • 1
    I can't know for sure, but it is most likely that viewController property contains nil. are you 100 % sure you did gameScene.viewController = self ? – Andrey Chernukha Sep 18 '16 at 13:31
  • yes I am its done on the GameViewController.swift whilst that code is on GameScene.swift –  Sep 18 '16 at 13:32
  • 1
    don't present alert controller in the init method. why do you do that? – Andrey Chernukha Sep 18 '16 at 13:37
  • where should I present it because its GameScene so I dont have viewDidload or viewDidAppear. Where do you recommend? –  Sep 18 '16 at 13:38
  • 1
    You need to do the following: 1) present alert at some point when you really need it, if you need to show alert at the very start then do that in the didMove(to:SKView) method 2) place gameScene.viewController = self right under let gameScene = GameScene(size: view.bounds.size) line. Now it will work – Andrey Chernukha Sep 18 '16 at 13:41
  • can you update your answer with all the explanation down here, in case others do have the same problem. – Lepidopteron May 04 '17 at 07:12