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

咨询:无Storyboard时在Xcode Swift Playground中切换多视图的方法

How to Switch Between Views in Swift Playground (No Storyboards)

Hey there! Let’s break down exactly how to switch between your three views in Swift Playground—no Storyboards required. I’ve worked through this scenario a bunch, so here’s a straightforward approach that’ll get you up and running quickly.

The core idea here is that Swift Playground uses PlaygroundPage.current.liveView to display your interface. Switching views just means updating this property with your next view, and we’ll use closures to let each view trigger those switches cleanly.


1. Prep Your View Files

First, let’s make sure each of your three views has a way to trigger a switch. We’ll add closure properties to each view so they can tell the main Playground code when to change views.

Example IntroView.swift

import UIKit

class IntroView: UIView {
    // Closure to trigger switch to game view
    var onNextTapped: (() -> Void)?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupIntroUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupIntroUI() {
        backgroundColor = .white
        
        // Add title text
        let titleLabel = UILabel()
        titleLabel.text = "Welcome to My Playground"
        titleLabel.font = .systemFont(ofSize: 24, weight: .bold)
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        addSubview(titleLabel)
        
        // Add "Start Game" button
        let nextButton = UIButton(type: .system)
        nextButton.setTitle("Start Game", for: .normal)
        nextButton.titleLabel?.font = .systemFont(ofSize: 18, weight: .semibold)
        nextButton.addTarget(self, action: #selector(nextButtonPressed), for: .touchUpInside)
        nextButton.translatesAutoresizingMaskIntoConstraints = false
        addSubview(nextButton)
        
        // Layout constraints
        NSLayoutConstraint.activate([
            titleLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
            titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor, constant: -50),
            
            nextButton.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 40),
            nextButton.centerXAnchor.constraint(equalTo: centerXAnchor)
        ])
    }
    
    @objc private func nextButtonPressed() {
        onNextTapped?() // Trigger the switch closure
    }
}

Example GameView.swift (SpriteKit)

import UIKit
import SpriteKit

class GameView: UIView {
    // Closures for back to intro and finish to conclusion
    var onBackTapped: (() -> Void)?
    var onFinishTapped: (() -> Void)?
    
    private var skView: SKView!
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupGameUI()
        setupSpriteKitScene()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupGameUI() {
        backgroundColor = .lightGray
        
        // Back button
        let backButton = UIButton(type: .system)
        backButton.setTitle("Back to Intro", for: .normal)
        backButton.addTarget(self, action: #selector(backButtonPressed), for: .touchUpInside)
        backButton.translatesAutoresizingMaskIntoConstraints = false
        addSubview(backButton)
        
        // Finish button
        let finishButton = UIButton(type: .system)
        finishButton.setTitle("Finish Game", for: .normal)
        finishButton.addTarget(self, action: #selector(finishButtonPressed), for: .touchUpInside)
        finishButton.translatesAutoresizingMaskIntoConstraints = false
        addSubview(finishButton)
        
        NSLayoutConstraint.activate([
            backButton.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 20),
            backButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
            
            finishButton.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 20),
            finishButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20)
        ])
    }
    
    private func setupSpriteKitScene() {
        // Add your SpriteKit scene here
        skView = SKView(frame: CGRect(x: 0, y: 80, width: bounds.width, height: bounds.height - 80))
        skView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(skView)
        
        let gameScene = SKScene(size: skView.bounds.size)
        gameScene.backgroundColor = .darkGray
        skView.presentScene(gameScene)
        
        // Example scene content (replace with your game logic)
        let gameLabel = SKLabelNode(text: "SpriteKit Game Area")
        gameLabel.fontSize = 20
        gameLabel.position = CGPoint(x: gameScene.size.width/2, y: gameScene.size.height/2)
        gameScene.addChild(gameLabel)
    }
    
    @objc private func backButtonPressed() {
        onBackTapped?()
    }
    
    @objc private func finishButtonPressed() {
        onFinishTapped?()
    }
}

Example ConclusionView.swift

import UIKit

class ConclusionView: UIView {
    // Closure to restart back to intro
    var onRestartTapped: (() -> Void)?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupConclusionUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupConclusionUI() {
        backgroundColor = .white
        
        let conclusionLabel = UILabel()
        conclusionLabel.text = "Game Completed!\nThanks for playing!"
        conclusionLabel.font = .systemFont(ofSize: 20, weight: .medium)
        conclusionLabel.textAlignment = .center
        conclusionLabel.numberOfLines = 0
        conclusionLabel.translatesAutoresizingMaskIntoConstraints = false
        addSubview(conclusionLabel)
        
        let restartButton = UIButton(type: .system)
        restartButton.setTitle("Start Over", for: .normal)
        restartButton.titleLabel?.font = .systemFont(ofSize: 18, weight: .semibold)
        restartButton.addTarget(self, action: #selector(restartButtonPressed), for: .touchUpInside)
        restartButton.translatesAutoresizingMaskIntoConstraints = false
        addSubview(restartButton)
        
        NSLayoutConstraint.activate([
            conclusionLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
            conclusionLabel.centerYAnchor.constraint(equalTo: centerYAnchor, constant: -50),
            
            restartButton.topAnchor.constraint(equalTo: conclusionLabel.bottomAnchor, constant: 40),
            restartButton.centerXAnchor.constraint(equalTo: centerXAnchor)
        ])
    }
    
    @objc private func restartButtonPressed() {
        onRestartTapped?()
    }
}

2. MyPlayground Code (The Entry Point)

Now in your main playground file, we’ll create a simple manager to handle all the view switching logic and set up the initial view.

import UIKit
import PlaygroundSupport

// Manager to handle view state and switches
class PlaygroundViewManager {
    private let introView: IntroView
    private let gameView: GameView
    private let conclusionView: ConclusionView
    
    init() {
        // Initialize all views with a standard iPhone-like frame
        let defaultFrame = CGRect(x: 0, y: 0, width: 375, height: 667)
        introView = IntroView(frame: defaultFrame)
        gameView = GameView(frame: defaultFrame)
        conclusionView = ConclusionView(frame: defaultFrame)
        
        // Connect all view switch closures
        setupSwitchCallbacks()
    }
    
    private func setupSwitchCallbacks() {
        // Intro → Game
        introView.onNextTapped = { [weak self] in
            guard let self = self else { return }
            PlaygroundPage.current.liveView = self.gameView
        }
        
        // Game → Intro
        gameView.onBackTapped = { [weak self] in
            guard let self = self else { return }
            PlaygroundPage.current.liveView = self.introView
        }
        
        // Game → Conclusion
        gameView.onFinishTapped = { [weak self] in
            guard let self = self else { return }
            PlaygroundPage.current.liveView = self.conclusionView
        }
        
        // Conclusion → Intro
        conclusionView.onRestartTapped = { [weak self] in
            guard let self = self else { return }
            PlaygroundPage.current.liveView = self.introView
        }
    }
    
    // Start the playground with the intro view
    func launch() {
        PlaygroundPage.current.liveView = introView
        // Keep the playground running so views stay interactive
        PlaygroundPage.current.needsIndefiniteExecution = true
    }
}

// Launch the view manager
let manager = PlaygroundViewManager()
manager.launch()

Critical Notes

  • needsIndefiniteExecution: This is mandatory! Without it, the playground will stop running immediately after initialization, and your views won’t be interactive.
  • Weak Self: Using [weak self] in closures prevents accidental memory leaks—good practice even in playgrounds.
  • SpriteKit Customization: Replace the example setupSpriteKitScene code with your actual game scene and logic; just make sure the SKView is properly sized and positioned.

That’s it! You’ll now be able to switch seamlessly between your three views.

内容的提问来源于stack exchange,提问作者CoderJ13

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:15:43