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

如何让文本冒险游戏在UITableView中逐行显示剧情单元格?

实现选择后动态添加UITableView剧情单元格的解决方案

我正在Xcode 12.4中使用UIKit开发一款iPhone文本冒险游戏,希望模仿手游《Lifeline》的剧情展示逻辑(剧情、动画、UI等会完全不同),但遇到了问题:不知道如何让用户做出选择后,UITableView在已有单元格下方生成新的剧情单元格。

(注:我正在学习Angela Wu的Udemy课程,剧情文本是练习用,并非抄袭,只为提升编码能力以便开发自己的应用。)

目前我的代码在应用加载时会一次性生成所有剧情单元格,而非用户做出选择后依次显示。我猜测需要修改UITableViewDataSource相关方法,但不知从何入手。请问如何实现用户做出选择后,在旧剧情下方生成新的剧情单元格?

原代码

import UIKit

class ViewController: UIViewController {

    
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var choice1Button: UIButton!
    @IBOutlet weak var choice2Button: UIButton!
    
    var storyNumber = 0
    
    var stories: [Story] = [
        Story(
            title: "Your car has blown a tire on a winding road in the middle of nowhere with no cell phone reception. You decide to hitchhike. A rusty pickup truck rumbles to a stop next to you. A man with a wide brimmed hat with soulless eyes opens the passenger door for you and asks: 'Need a ride, boy?'.",
            choice1: "I'll hop in. Thanks for the help!", choice1Destination: 2,
            choice2: "Better ask him if he's a murderer first.", choice2Destination: 1
        ),
        Story(
            title: "He nods slowly, unfazed by the question.",
            choice1: "At least he's honest. I'll climb in.", choice1Destination: 2,
            choice2: "Wait, I know how to change a tire.", choice2Destination: 3
        ),
        Story(
            title: "As you begin to drive, the stranger starts talking about his relationship with his mother. He gets angrier and angrier by the minute. He asks you to open the glovebox. Inside you find a bloody knife, two severed fingers, and a cassette tape of Elton John. He reaches for the glove box.",
            choice1: "I love Elton John! Hand him the cassette tape.", choice1Destination: 5,
            choice2: "It's him or me! You take the knife and stab him.", choice2Destination: 4
        ),
        Story(
            title: "What? Such a cop out! Did you know traffic accidents are the second leading cause of accidental death for most adult age groups?",
            choice1: "The", choice1Destination: 0,
            choice2: "End", choice2Destination: 0
        ),
        Story(
            title: "As you smash through the guardrail and careen towards the jagged rocks below you reflect on the dubious wisdom of stabbing someone while they are driving a car you are in.",
            choice1: "The", choice1Destination: 0,
            choice2: "End", choice2Destination: 0
        ),
        Story(
            title: "You bond with the murderer while crooning verses of 'Can you feel the love tonight'. He drops you off at the next town. Before you go he asks you if you know any good places to dump bodies. You reply: 'Try the pier.'",
            choice1: "The", choice1Destination: 0,
            choice2: "End", choice2Destination: 0
        )
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.dataSource = self
        
        tableView.register(UINib(nibName: "StoryCell", bundle: nil), forCellReuseIdentifier: "ReusableCell")
        
        updateUI()
    }
    
    
    @IBAction func choiceMade(_ sender: UIButton) {
        
        var  userChoice = sender.currentTitle!
        
        if userChoice == stories[storyNumber].choice1 {
            storyNumber = stories[storyNumber].choice1Destination
        }
        
        if userChoice == stories[storyNumber].choice2 {
            storyNumber = stories[storyNumber].choice2Destination
        }
        
        updateUI()
    }
    
    func updateUI() {
        
        choice1Button.setTitle(stories[storyNumber].choice1, for: .normal)
        choice2Button.setTitle(stories[storyNumber].choice2, for: .normal)
        
        tableView.reloadData()
    }
    
}

extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return stories.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ReusableCell", for: indexPath) as! StoryCell
        cell.label.text = stories[indexPath.row].title
        
        return cell
    }
}

修改方案

核心逻辑是:维护一个已展示剧情数组,每次用户选择后将对应新剧情追加到数组,再通知表格插入单个单元格,而非刷新整个列表。

1. 添加已展示剧情数组

在ViewController类中新增变量:

var displayedStories: [Story] = []

2. 初始化加载第一个剧情

修改viewDidLoad,启动时将初始剧情加入已展示数组:

override func viewDidLoad() {
    super.viewDidLoad()
    
    tableView.dataSource = self
    tableView.register(UINib(nibName: "StoryCell", bundle: nil), forCellReuseIdentifier: "ReusableCell")
    
    // 初始化添加首个剧情
    displayedStories.append(stories[0])
    updateUI()
}

3. 调整UITableView数据源方法

将表格数据源从完整剧情数组切换为已展示数组:

extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return displayedStories.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ReusableCell", for: indexPath) as! StoryCell
        cell.label.text = displayedStories[indexPath.row].title
        
        return cell
    }
}

4. 修改选择事件处理逻辑

更新choiceMade方法,追加新剧情并插入表格单元格,同时自动滚动到底部:

@IBAction func choiceMade(_ sender: UIButton) {
    guard let userChoice = sender.currentTitle else { return }
    var nextStoryIndex = 0
    
    if userChoice == stories[storyNumber].choice1 {
        nextStoryIndex = stories[storyNumber].choice1Destination
    } else if userChoice == stories[storyNumber].choice2 {
        nextStoryIndex = stories[storyNumber].choice2Destination
    }
    
    // 更新当前剧情索引并追加新剧情
    storyNumber = nextStoryIndex
    let newStory = stories[storyNumber]
    displayedStories.append(newStory)
    
    updateUI()
    
    // 插入新单元格并滚动至底部
    let newIndexPath = IndexPath(row: displayedStories.count - 1, section: 0)
    tableView.insertRows(at: [newIndexPath], with: .automatic)
    tableView.scrollToRow(at: newIndexPath, at: .bottom, animated: true)
}

5. 优化UI更新逻辑

修改updateUI,仅更新按钮文字,结局时隐藏选择按钮:

func updateUI() {
    choice1Button.setTitle(stories[storyNumber].choice1, for: .normal)
    choice2Button.setTitle(stories[storyNumber].choice2, for: .normal)
    
    // 结局剧情隐藏选择按钮
    let isEnding = storyNumber == 3 || storyNumber == 4 || storyNumber == 5
    choice1Button.isHidden = isEnding
    choice2Button.isHidden = isEnding
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 06:55:16