如何为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
相关产品推荐
相关产品推荐

