SpriteKit场景init方法添加UITextField子视图不显示问题
问题根因
在SceneMenu的init()方法里调用view?.addSubview(textField)不生效的核心原因是:SKScene初始化阶段,它的view属性还未完成赋值。
- 你在
GameViewController的viewDidLoad里先创建了SceneMenu实例,这时候只走了SceneMenu的init流程,scene还没被挂载到SKView上,self.view为nil,所有对view的操作都不会执行 - 等到用户点击触发
touchesBegan时,scene早就通过presentScene完成了和SKView的绑定,self.view有了有效值,这时候调用addSubview自然能正常显示
解决方案
不要在init方法里添加UIKit子视图,重写SKScene的生命周期方法didMove(to view: SKView),这个方法会在scene被正式present到SKView上之后由系统自动调用,此时view属性一定有效,在这里添加UITextField就能实现启动后直接展示,不需要用户点击。
修正后的代码
GameViewController.swift不需要做任何修改,保持原有逻辑即可,只需要调整SceneMenu.swift的实现:
class SceneMenu: SKScene { // 建议把textField存为属性,方便后续获取输入内容、调整布局 private var inputTextField: UITextField? override init(size: CGSize) { super.init(size: size) // init阶段只添加SpriteKit原生节点即可,不要操作UIKit控件 let btnAlert = SKLabelNode(text: "Show TextField") btnAlert.name = "btn_text" btnAlert.fontSize = 20 btnAlert.fontColor = SKColor.blue btnAlert.fontName = "Avenir" btnAlert.position = CGPoint(x: size.width / 2, y: size.height / 2) addChild(btnAlert) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } // 系统回调:scene已经被添加到SKView上时自动触发 override func didMove(to view: SKView) { super.didMove(to: view) // 在这里初始化添加UITextField,启动后会直接显示 setupTextField() } private func setupTextField() { // 注意:UIKit坐标原点在屏幕左上角,SpriteKit坐标原点在屏幕左下角,布局时注意y轴位置适配 let textFieldFrame = CGRect(x: 100, y: 100, width: 200, height: 30) let textField = UITextField(frame: textFieldFrame) textField.backgroundColor = SKColor.blue textField.placeholder = "Type name" textField.textColor = .white view.addSubview(textField) self.inputTextField = textField } func showTextField() { let textFieldFrame = CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 200, height: 30)) let textField = UITextField(frame: textFieldFrame) textField.backgroundColor = SKColor.blue textField.placeholder = "Type name" view?.addSubview(textField) } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { if let touch = touches.first { let location = touch.location(in: self) let action = atPoint(location) if action.name == "btn_text" { showTextField() } } } }
额外注意点
- 如果后续需要切换scene,记得在
willMove(from view: SKView)方法里把添加的UITextField从父视图移除,避免内存泄漏或者控件残留到其他scene上 - UIKit控件的交互优先级高于SpriteKit的手势响应,如果textField覆盖了部分SpriteKit节点,点击对应区域会优先触发textField的输入,不会响应
touchesBegan
内容的提问来源于stack exchange,提问作者Sebastijan B.
相关产品推荐
相关产品推荐

