咨询:无Storyboard时在Xcode Swift Playground中切换多视图的方法
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
setupSpriteKitScenecode 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

