如何为TableView Cell中TextField的字号变化添加动画?
问题描述
我有一个包含Label和TextField的TableView Cell。在Storyboard中,TextField的宽度约束设为150,文本使用System Bold 30号字体,开启了「Adjust to fit」,最小字号设为10。
我通过以下代码在TextField开始/结束编辑时,动画调整其宽度约束:
func textFieldDidBeginEditing(_ textField: UITextField) { UIView.animate(withDuration: 1) { [self] in textFieldWidth.constant = 300 self.layoutIfNeeded() } } func textFieldDidEndEditing(_ textField: UITextField) { UIView.animate(withDuration: 1) { [self] in textFieldWidth.constant = 150 self.layoutIfNeeded() } }
问题:TextField的字号会直接跳变为30号,没有动画效果;恢复宽度时也是瞬间变化。我试过UIView.transition、带scaleX/scaleY的transform等方法,但效果都不好——比如仅用scaleX缩放会导致文本变形,我需要保持文本宽高比。
期望效果:字号变化(从小号到30号)能和TextField宽度变化同步动画,动画方向从尾部向左。如果无法通过TextField实现,是否有诸如用「假Label」之类的替代方案实现平滑的字号变化?
解决方案
方案一:手动绑定宽度与字号的动画关系
TextField的「Adjust to fit」是系统自动计算字号,不会触发动画,因此需要手动控制字号随宽度的变化比例:
- 关闭TextField的「Adjust to fit」,初始字号设为10(对应宽度150时的最小字号)。
- 在动画闭包中,根据当前宽度的比例实时计算字号,同步更新TextField的
font属性:
func textFieldDidBeginEditing(_ textField: UITextField) { let startWidth = 150.0 let targetWidth = 300.0 let startFontSize = 10.0 let targetFontSize = 30.0 UIView.animate(withDuration: 1.0) { let widthProgress = (self.textFieldWidth.constant - startWidth) / (targetWidth - startWidth) let currentFontSize = startFontSize + (targetFontSize - startFontSize) * widthProgress textField.font = .systemFont(ofSize: currentFontSize, weight: .bold) self.textFieldWidth.constant = targetWidth self.layoutIfNeeded() } } func textFieldDidEndEditing(_ textField: UITextField) { let startWidth = 300.0 let targetWidth = 150.0 let startFontSize = 30.0 let targetFontSize = 10.0 UIView.animate(withDuration: 1.0) { let widthProgress = (startWidth - self.textFieldWidth.constant) / (startWidth - targetWidth) let currentFontSize = startFontSize - (startFontSize - targetFontSize) * widthProgress textField.font = .systemFont(ofSize: currentFontSize, weight: .bold) self.textFieldWidth.constant = targetWidth self.layoutIfNeeded() } }
方案二:用Label模拟TextField动画(推荐)
如果TextField自带特性难以满足需求,可以用「展示Label+透明输入TextField」的组合实现完全可控的动画:
- 在Cell中添加两个控件:
displayLabel:负责展示带动画的文本,开启「Adjust to fit」,最小字号10,初始宽度150,字体为30号Bold,对齐方式设为右对齐(实现从尾部向左的动画方向)。inputTextField:透明背景、无边框,覆盖在displayLabel上方,仅负责接收用户输入。
- 动画逻辑:
// 编辑开始时的动画 func textFieldDidBeginEditing(_ textField: UITextField) { UIView.animate(withDuration: 1.0) { self.displayLabelWidth.constant = 300 // 根据宽度比例计算字号,从10平滑过渡到30 let widthRatio = self.displayLabelWidth.constant / 150.0 let fontSize = 10 + (30 - 10) * (widthRatio - 1) self.displayLabel.font = .systemFont(ofSize: fontSize, weight: .bold) self.inputTextFieldWidth.constant = 300 self.layoutIfNeeded() } } // 编辑结束时的动画 func textFieldDidEndEditing(_ textField: UITextField) { // 同步输入文本到展示Label self.displayLabel.text = textField.text UIView.animate(withDuration: 1.0) { self.displayLabelWidth.constant = 150 let widthRatio = self.displayLabelWidth.constant / 300.0 let fontSize = 30 - (30 - 10) * (1 - widthRatio) self.displayLabel.font = .systemFont(ofSize: fontSize, weight: .bold) self.inputTextFieldWidth.constant = 150 self.layoutIfNeeded() } }
这种方式完全由开发者控制字号和宽度的动画节奏,不会出现系统自动调整导致的跳变,同时保持输入功能正常。
补充优化
- 若要强化「从尾部向左」的动画方向,确保Label和TextField的文本对齐方式为右对齐,动画时宽度从右侧扩展,文本自然向左展开。
- 使用
UIViewPropertyAnimator替代普通UIView.animate,可以添加弹簧效果、中途暂停/恢复动画等更精细的控制。
内容的提问来源于stack exchange,提问作者artexhibit
相关产品推荐
相关产品推荐

