cdunn2013
cdunn2013

Reputation: 214

How to call a UITextField inside of SpriteKit?

As the question states, I am attempting to call on a UITextField inside of SpriteKit to accept a user input for a name. I've looked at many posts on here but none of them seem to apply to the issue I am facing. The first thing I am doing is declaring the UITextField as a class variable, so I can remove it from view in a separate function later by doing the following:

let nameEntry = UITextField(frame: CGRect(origin: CGPoint(x: 800, y: 875), size: CGSize(width: 600, height: 200)))

then I go on to add the UITextField by stating:

self.view?.addSubview(nameEntry)

However, the text box does not show up in my scene. I've looked at it in hierarchy view and it is simply not there. I am fairly inexperienced at using SceneKit so I'm curious to see what I'm doing wrong. Thanks in advance for anyone who attempts to help!

for a better look at how I am going about this:

class Tutorial : SKScene{
let nameEntry = UITextField(frame: CGRect(origin: CGPoint(x: 800, y: 875), size: CGSize(width: 600, height: 200)))

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
//insert many lines of code here
self.view?.addSubview(nameEntry)
  }
}

EDIT: Here's all of the code leading up to this point.

class Tutorial : SKScene{
var chatBoxInt : Int = 0
let chatBoxLabel : SKLabelNode = SKLabelNode(text: "")
let arrow = SKSpriteNode(imageNamed: "Arrow")
let HappinessIcon = SKSpriteNode(imageNamed: "Happiness Icon")
let IntelligenceIcon = SKSpriteNode(imageNamed: "IntelligenceIcon")
let HealthIcon = SKSpriteNode(imageNamed: "HealthIcon")
let chatBox = SKSpriteNode(imageNamed: "ChatBox")
let maleButtonBackground = SKSpriteNode(imageNamed: "ButtonBackground")
let femaleButtonBackground = SKSpriteNode(imageNamed: "ButtonBackground")
let selectedGenderButtonBackground = SKSpriteNode(imageNamed: "ButtonBackgroundSelected")
let femaleLabel = SKLabelNode(text: "Female")
let maleLabel = SKLabelNode(text: "Male")
let genderLabel = SKLabelNode(text: "Gender:")
let nameLabel = SKLabelNode(text: "Name:")
let nameEntry = UITextField(frame: CGRect(origin: CGPoint(x: 800, y: 875), size: CGSize(width: 600, height: 200)))
enum genders {
    case Male
    case Female
}
var genderSelected : genders = .Male


override func didMove(to view: SKView) {
    let background = SKSpriteNode(imageNamed: "Background")
    background.size = CGSize(width: self.size.width, height: self.size.height)
    background.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
    background.zPosition = 0
    scene?.addChild(background)
    Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(beginTutorial), userInfo: nil, repeats: false)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    for touch : AnyObject in touches{
        let pointOfTouch = touch.location(in: self)
        if(maleButtonBackground.contains(pointOfTouch) || femaleButtonBackground.contains(pointOfTouch)){
            if(maleButtonBackground.contains(pointOfTouch)){
                swapGenderButton(ImageToSwap: maleButtonBackground)
                genderSelected = .Male
            }
            if(femaleButtonBackground.contains(pointOfTouch)){
                swapGenderButton(ImageToSwap: femaleButtonBackground)
                genderSelected = .Female
            }
        }else{
            chatBoxInt += 1
            let fadeInAnimation = SKAction.fadeIn(withDuration: 1)
            let fadeOutAnimation = SKAction.fadeOut(withDuration: 1)
            let removeAction = SKAction.removeFromParent()
            let fadeOutSequence = SKAction.sequence([fadeOutAnimation, removeAction])
            switch chatBoxInt {
            case 1:
                ChangeText(LabelText: "Best Life is the place for you to make the life you've always dreamed of!")
            case 2:
                ChangeText(LabelText: "In Best Life you can be who you want to be, do what you want to do... as long as you don't die.")
            case 3:
                ChangeText(LabelText: "The goal of Best Life is simple, make the best life possible for yourself before your time runs out.")
            case 4:
                ChangeText(LabelText: "Let's go over some of the basics of best life:")
            case 5:
                ChangeText(LabelText: "When you get into your new life, you'll have a HUD at the bottom of your screen at all times. This HUD will act as your guide as you go throughout Best Life. Let's get familiar with the icons and what they mean.")
            case 6:
                ChangeText(LabelText: "This is your happiness indicator, it will show you how much you are enjoying your life, try and keep this high at all times... too little happiness can result in your alter ego committing suicide.")
                HappinessIcon.position = CGPoint(x: self.size.width/2, y: (self.size.height/2) - 100)
                HappinessIcon.size = CGSize(width: 200, height: 200)
                HappinessIcon.zPosition = 2
                HappinessIcon.alpha = 0
                self.addChild(HappinessIcon)
                HappinessIcon.run(fadeInAnimation)
            case 7:
                HappinessIcon.run(fadeOutSequence)
                ChangeText(LabelText: "This is your intelligence indicator... it shows... well.... intelligence... what else would it do...? Too little of this and you may not get good jobs; leading to lower income.")
                IntelligenceIcon.position = CGPoint(x: self.size.width/2, y: (self.size.height/2) - 100)
                IntelligenceIcon.size = CGSize(width: 200, height: 200)
                IntelligenceIcon.zPosition = 2
                IntelligenceIcon.alpha = 0
                self.addChild(IntelligenceIcon)
                IntelligenceIcon.run(fadeInAnimation)
            case 8:
                IntelligenceIcon.run(fadeOutSequence)
                ChangeText(LabelText: "This is your health icon. It shows your current overall health level. If this gets too low it can contribute to life threatning illnesses which can ultimately lead to your demise.")
                HealthIcon.position = CGPoint(x: self.size.width/2, y: (self.size.height/2) - 100)
                HealthIcon.size = CGSize(width: 200, height: 200)
                HealthIcon.zPosition = 2
                HealthIcon.alpha = 0
                self.addChild(HealthIcon)
                HealthIcon.run(fadeInAnimation)
            case 9:
                HealthIcon.run(fadeOutSequence)
                ChangeText(LabelText: "The last thing that you need to know is that one day in your world will pass one year in your alter ego's life. Make sure you are making the most out of every day to make the best life possible for your alter ego!")
            case 10:
                ChangeText(LabelText: "Alright, that pretty much sums it up. The world is yours for the taking, go seize it! Have fun in your new Best Life!")
            case 11:
                ChangeText(LabelText: "First, we'll need to we'll need to set up your new alter ego. Some of these options can only be accessed by purchasing them, but since this is your first go around I'll cover this one for you.")
                chatBoxLabel.run(SKAction.moveTo(y: 1750, duration: 1))
                let changeSize = SKAction.scale(to: CGSize(width: 800, height: 1600), duration: 1)
                chatBox.run(changeSize)
                arrow.run(fadeOutSequence)
                genderLabel.position = CGPoint(x: 500, y: 1300)
                genderLabel.fontColor = .black
                genderLabel.zPosition = 2
                genderLabel.fontSize = 45
                genderLabel.alpha = 0
                self.addChild(genderLabel)
                genderLabel.run(fadeInAnimation)
                maleButtonBackground.position = CGPoint(x: 575, y: 1175)
                maleButtonBackground.zPosition = 2
                maleButtonBackground.setScale(0.3)
                maleButtonBackground.alpha = 0
                self.addChild(maleButtonBackground)
                maleButtonBackground.run(fadeInAnimation)
                femaleButtonBackground.position = CGPoint(x: 955, y: 1175)
                femaleButtonBackground.zPosition = 2
                femaleButtonBackground.setScale(0.3)
                femaleButtonBackground.alpha = 0
                self.addChild(femaleButtonBackground)
                femaleButtonBackground.run(fadeInAnimation)
                maleLabel.fontSize = 45
                maleLabel.fontColor = .white
                maleLabel.zPosition = 4
                maleLabel.alpha = 0
                maleLabel.position = CGPoint(x: 575, y: 1162)
                self.addChild(maleLabel)
                maleLabel.run(fadeInAnimation)
                femaleLabel.fontSize = 45
                femaleLabel.fontColor = .white
                femaleLabel.zPosition = 4
                femaleLabel.alpha = 0
                femaleLabel.position = CGPoint(x: 955, y: 1162)
                self.addChild(femaleLabel)
                femaleLabel.run(fadeInAnimation)
                nameLabel.position = CGPoint(x: 500, y: 1000)
                nameLabel.fontSize = 45
                nameLabel.fontColor = .black
                nameLabel.alpha = 0
                nameLabel.zPosition = 3
                self.addChild(nameLabel)
                nameLabel.run(fadeInAnimation)
                nameEntry.backgroundColor = .white
                self.view?.addSubview(nameEntry)

                default:
                return
            }
        }
    }

}

Upvotes: 1

Views: 581

Answers (1)

Josh Homann
Josh Homann

Reputation: 16327

SpriteKit's SKSKScene exists inside of UIKit's SKView. ie your whole Spritekit scene is basically the backing layer for a single view in your app. If you want to do things in the UIKit layer it makes more sense to do them at the level of your view controller and delegate the responsibility back. So, for instance if you want a textField you can set it up in interface builder and make it hidden, then show it only when your scene requests it.

If you don't want to do it this way, you can still reach up into your view as you are doing and add subviews from your SKScene (its bad OOP to reach up into and alter your parent, but nothing in UIKit prevents you from doing it). Most likely the issue you are having is that the coordinate systems in SpriteKit and UIKit are different and they are actually upside down. So you cannot just use your SKScene coordinates for UIViews. You need to convert. SKView has a family of methods that will do the math for you: SKView.convert(:to:) SkView.convert(:from:). Note that that only gets you as far as your SKView. You then need to convert to your viewController.view coordinates using UIKit's convert methods.

I think its far less confusing if you leave the UIKit stuff in the UIKit layer and just have the viewController and scene pass the relevant information back and forth.

EDIT: here is an example

import PlaygroundSupport
import SpriteKit

class GameScene: SKScene {

    private lazy var textField: UITextField = {
        let textField = UITextField()
        textField.frame.size = CGSize(width: 100, height: 30)
        textField.backgroundColor = .cyan
        return textField
    }()

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let view = view,
              let point = touches.first?.location(in: self) else {
            return
        }
        if textField.superview != nil {
            textField.removeFromSuperview()
        }
        textField.center = view.convert(point, from: self)
        view.addSubview(textField)
        textField.becomeFirstResponder()
    }  
}

let sceneView = SKView(frame: CGRect(x:0 , y:0, width: 640, height: 480))
if let scene = GameScene(fileNamed: "GameScene") {
    scene.scaleMode = .aspectFill
    sceneView.presentScene(scene)
}

PlaygroundSupport.PlaygroundPage.current.liveView = sceneView

Upvotes: 1

Related Questions