Swift 4中如何动态修改代码创建的自定义inputAccessoryView高度?
解决消息应用输入框高度随文本行数动态增加的动画问题
嘿,刚好我之前做过类似的消息输入框需求,给你一步步拆解怎么实现:
核心思路
因为typingView是作为主视图的inputAccessoryView,系统对它的布局有特殊管理,所以我们需要通过监听文本框内容变化,计算所需高度,更新约束并配合动画触发布局更新,而且只处理高度增加的情况(你说暂不考虑减少,那我们就只做增量判断)。
具体实现步骤
1. 先做好基础配置
首先确保你的messageTextView已经设置好代理,并且关闭滚动(这样文本内容超出一行时,才会让整个输入框增高,而不是文本框内部滚动):
override func viewDidLoad() { super.viewDidLoad() messageTextView.delegate = self messageTextView.isScrollEnabled = false // 记得给typingViewHeight设置初始值,比如等于文本框默认高度+上下内边距 typingViewHeight?.constant = (messageTextView.font?.lineHeight ?? 20) + 16 // 16是上下padding }
2. 监听文本内容变化,计算并更新高度
实现UITextViewDelegate的textViewDidChange方法,在这里处理高度计算和动画:
extension YourViewController: UITextViewDelegate { func textViewDidChange(_ textView: UITextView) { // 计算文本框刚好容纳内容的高度,sizeThatFits会自动考虑字体、行间距等 let requiredHeight = textView.sizeThatFits( CGSize(width: textView.bounds.width, height: CGFloat.greatestFiniteMagnitude) ).height // 只处理高度增加的情况 guard let currentHeight = typingViewHeight?.constant, requiredHeight > currentHeight else { return } // 更新约束值 typingViewHeight?.constant = requiredHeight // 执行动画,这里的关键是在动画块里调用layoutIfNeeded触发布局更新 UIView.animate(withDuration: 0.2, curve: .easeInOut) { self.typingView.layoutIfNeeded() self.view.layoutIfNeeded() // 额外保险:如果系统没自动更新inputAccessoryView,触发一下响应状态切换 self.becomeFirstResponder() self.resignFirstResponder() } } }
3. 确保输入框内部布局正确
你的typingView内部布局要做好绑定,比如:
messageTextView的上下左右约束和typingView绑定(留好内边距)sendButton固定在右下角(比如右内边距、垂直居中,高度宽度固定)
这样当typingView高度增加时,messageTextView会自动扩展高度,按钮位置保持不变。
关键注意点
- 一定要关闭
messageTextView的滚动:isScrollEnabled = false,否则文本框会自己滚动,不会触发整个输入框增高 - 用
sizeThatFits计算高度是最准确的,它会根据文本框的当前样式(字体、行间距、内边距)计算刚好容纳内容的高度 - 动画块里必须调用
layoutIfNeeded,否则约束变化不会有动画效果 - 最后触发
becomeFirstResponder和resignFirstResponder是为了确保系统正确更新inputAccessoryView的高度,避免出现布局异常
内容的提问来源于stack exchange,提问作者Michael Hsu
相关产品推荐
相关产品推荐

