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

如何为UITableViewCell内的文本实现打字机式动画效果?

实现UITableViewCell打字机动画方案

代码结构选择:自定义UITableViewCell + 内置动画方法

推荐把打字机动画逻辑封装在自定义UITableViewCell中,而非ViewController。因为打字机效果属于Cell的展示行为(View层逻辑),放在Cell里能让ViewController专注于数据管理和业务流程,代码更模块化、复用性更强,符合MVC设计原则。


具体实现步骤

1. 自定义剧情文本Cell

创建专门的StoryTextCell,内置UILabel和打字机动画方法:

import UIKit

class StoryTextCell: UITableViewCell {
    static let reuseID = "StoryTextCell"
    
    // 剧情文本标签
    private let storyLabel: UILabel = {
        let label = UILabel()
        label.numberOfLines = 0
        label.font = .systemFont(ofSize: 16)
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupSubviews()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // 布局标签
    private func setupSubviews() {
        contentView.addSubview(storyLabel)
        NSLayoutConstraint.activate([
            storyLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16),
            storyLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            storyLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
            storyLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -16)
        ])
    }
    
    // 打字机动画核心方法
    func startTypingAnimation(with fullText: String, speed: TimeInterval = 0.1) {
        storyLabel.text = ""
        var charIndex = 0
        let characters = Array(fullText)
        
        Timer.scheduledTimer(withTimeInterval: speed, repeats: true) { [weak self] timer in
            guard let self = self, charIndex < characters.count else {
                timer.invalidate()
                return
            }
            self.storyLabel.text?.append(characters[charIndex])
            charIndex += 1
        }
    }
}

2. ViewController中处理剧情推进与Cell插入

在ViewController里管理剧情数据,处理按钮点击事件,插入新Cell并触发动画:

import UIKit

class StoryGameVC: UIViewController, UITableViewDataSource {
    private let tableView = UITableView()
    private var storyData: [String] = ["欢迎来到剧情游戏,请做出你的选择"]
    private let optionBtn1 = UIButton(type: .system)
    private let optionBtn2 = UIButton(type: .system)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        tableView.register(StoryTextCell.self, forCellReuseIdentifier: StoryTextCell.reuseID)
        tableView.dataSource = self
    }
    
    // 基础UI布局(按需调整)
    private func setupUI() {
        view.addSubview(tableView)
        view.addSubview(optionBtn1)
        view.addSubview(optionBtn2)
        
        // 设置按钮属性与点击事件
        optionBtn1.setTitle("选项A", for: .normal)
        optionBtn2.setTitle("选项B", for: .normal)
        optionBtn1.addTarget(self, action: #selector(optionSelected(_:)), for: .touchUpInside)
        optionBtn2.addTarget(self, action: #selector(optionSelected(_:)), for: .touchUpInside)
        
        // 这里省略tableView和按钮的约束布局代码,按你的界面需求编写
    }
    
    // 选项点击逻辑
    @objc private func optionSelected(_ sender: UIButton) {
        // 根据选择生成新剧情文本
        let newStory = sender == optionBtn1 ? "选择A后,你进入了一条幽暗的走廊..." : "选择B后,你发现了一扇隐藏的门..."
        storyData.append(newStory)
        
        // 插入新Cell
        let newIndexPath = IndexPath(row: storyData.count - 1, section: 0)
        tableView.beginUpdates()
        tableView.insertRows(at: [newIndexPath], with: .fade)
        tableView.endUpdates()
        
        // 滚动到新Cell,延迟触发动画确保Cell已显示
        tableView.scrollToRow(at: newIndexPath, at: .bottom, animated: true)
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
            guard let self = self, let cell = self.tableView.cellForRow(at: newIndexPath) as? StoryTextCell else { return }
            cell.startTypingAnimation(with: newStory)
        }
    }
    
    // MARK: UITableView数据源方法
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return storyData.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: StoryTextCell.reuseID, for: indexPath) as! StoryTextCell
        // 旧Cell直接显示完整文本,仅新插入的Cell触发动画
        if indexPath.row != storyData.count - 1 {
            cell.storyLabel.text = storyData[indexPath.row]
        }
        return cell
    }
}

关键细节说明

  • 用Timer实现逐字添加文本,通过weak self避免循环引用;
  • 插入Cell后延迟0.3秒触发动画,确保Cell完成插入动画并滚动到可视区域;
  • 动画速度可通过startTypingAnimation的speed参数调整;
  • 旧Cell直接显示完整文本,避免复用时重复触发动画。

内容的提问来源于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.19 06:35:11