You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 08:24:22