如何在UITableViewCell的UILabel逐字输入文本时扩展其背景?
我在自定义UITableViewCell(关联StoryCell.xib和StoryCell.swift)中实现了UILabel的逐字输入动画,并且给Label加了背景效果。但现在的问题是,文本逐字输入时,Label的背景没法跟着每一行文本扩展——要么背景直接提前适配完整文本的尺寸,要么就只保持单行,把换行后的文本截断。调整过代码、属性和约束都没解决,求解决思路。
TL;DR:想让自定义单元格里的UILabel背景随逐字输入的文本逐行扩展,怎么实现?
参考代码
import UIKit class StoryCell: UITableViewCell { var typingTimer: Timer? // 打字动画计时器 @IBOutlet weak var label: UILabel! override func awakeFromNib() { super.awakeFromNib() // 初始化代码 } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) // 选中状态配置 } // 启动打字动画 func startTypingAnimation(withText text: String) { label.text = "" // 清空现有文本 var charIndex = 0 // 确保之前的计时器已停止 typingTimer?.invalidate() typingTimer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { timer in if charIndex < text.count { let index = text.index(text.startIndex, offsetBy: charIndex) let nextChar = String(text[index]) self.label.text?.append(nextChar) charIndex += 1 } else { timer.invalidate() } } } }
注:Label的文本来自其他.swift文件,上述代码未体现文本内容。
解决思路
先把Label的基础配置和约束做对
- 在
StoryCell.xib里,给Label设置:numberOfLines设为0(支持多行显示)lineBreakMode设为.byWordWrapping(按单词换行)- 约束要和cell的contentView绑定:比如左右各留固定边距,上下也留边距,不要给Label设置固定宽高
- 把
preferredMaxLayoutWidth设为Automatic(xib中可直接勾选),让Label自动根据父容器宽度计算每行最大宽度
- 如果是代码里配置,对应的代码:
label.numberOfLines = 0 label.lineBreakMode = .byWordWrapping label.preferredMaxLayoutWidth = UIScreen.main.bounds.width - 20 // 假设左右边距各10
- 在
每次追加字符后强制更新布局
现在的代码只是追加字符,但系统不会自动实时更新Label的尺寸和cell高度。需要在追加字符后手动触发布局更新,修改startTypingAnimation里的计时器闭包:typingTimer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { timer in if charIndex < text.count { let index = text.index(text.startIndex, offsetBy: charIndex) let nextChar = String(text[index]) self.label.text?.append(nextChar) charIndex += 1 // 强制更新Label布局,让它重新计算当前文本的尺寸 self.label.setNeedsLayout() self.label.layoutIfNeeded() // 更新cell的布局,同时通知tableView刷新高度 self.contentView.setNeedsLayout() self.contentView.layoutIfNeeded() if let tableView = self.superview as? UITableView { tableView.beginUpdates() tableView.endUpdates() } } else { timer.invalidate() } }这里用
beginUpdates()和endUpdates()可以让tableView平滑调整当前cell的高度,避免出现跳动。不要提前加载完整文本
绝对不能先把完整文本赋值给Label再隐藏逐字显示——这种操作会让Label提前计算好完整尺寸,背景直接撑满,完全达不到逐行扩展的效果。必须保持Label的文本是实时追加的状态。检查背景的绑定方式
如果是用label.backgroundColor,只要Label尺寸正确,背景会自动跟着扩展。如果是用自定义的背景视图(比如UIView作为Label的背景),必须给背景视图设置和Label边缘完全绑定的约束(leading/trailing/top/bottom都等于Label的对应属性),不能给背景视图设置固定宽高。
内容的提问来源于stack exchange,提问作者nascent_swifter

