Swift自定义文本输入视图实现求助:仿消息应用交互
嘿,这个需求可是消息类APP的标准配置啦,我帮你拆解成几个关键环节,一步步实现就没问题:
一、先搭好自定义输入视图的基础结构
首先创建一个CustomInputView的自定义UIView子类,里面要包含三个核心元素:
UITextView:用它代替UITextField,因为要支持多行文本和高度自适应- 上传按钮(UIButton):用来触发文本上传操作
- 基础布局约束:确保元素排列合理,初始状态贴紧屏幕底部
布局的时候用Auto Layout,这里给你个简化版的代码示例:
class CustomInputView: UIView, UITextViewDelegate { private let textView = UITextView() private let uploadButton = UIButton(type: .system) // 用来限制TextView最大高度的约束 private var textViewHeightConstraint: NSLayoutConstraint? override init(frame: CGRect) { super.init(frame: frame) setupViews() setupConstraints() setupNotifications() } required init?(coder: NSCoder) { super.init(coder: coder) setupViews() setupConstraints() setupNotifications() } private func setupViews() { backgroundColor = .systemBackground // 配置TextView textView.font = .systemFont(ofSize: 16) textView.isScrollEnabled = false // 禁用滚动,让它自动撑高 textView.delegate = self textView.textContainerInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8) // 增加内边距,避免文字贴边 addSubview(textView) // 配置上传按钮 uploadButton.setTitle("上传", for: .normal) uploadButton.addTarget(self, action: #selector(uploadTapped), for: .touchUpInside) addSubview(uploadButton) // 给TextView加占位符(UITextView默认没有这个功能) let placeholderLabel = UILabel() placeholderLabel.text = "输入消息..." placeholderLabel.textColor = .lightGray placeholderLabel.translatesAutoresizingMaskIntoConstraints = false textView.addSubview(placeholderLabel) NSLayoutConstraint.activate([ placeholderLabel.leadingAnchor.constraint(equalTo: textView.leadingAnchor, constant: 8), placeholderLabel.topAnchor.constraint(equalTo: textView.topAnchor, constant: 8) ]) // 监听文字变化,控制占位符显示/隐藏 textView.addObserver(self, forKeyPath: "text", options: .new, context: nil) } private func setupConstraints() { textView.translatesAutoresizingMaskIntoConstraints = false uploadButton.translatesAutoresizingMaskIntoConstraints = false let constraints = [ // 上传按钮约束:右对齐、垂直居中、固定宽高 uploadButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8), uploadButton.centerYAnchor.constraint(equalTo: centerYAnchor), uploadButton.widthAnchor.constraint(equalToConstant: 44), uploadButton.heightAnchor.constraint(equalToConstant: 32), // TextView约束:左对齐、和按钮间距、上下贴紧输入视图 textView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8), textView.trailingAnchor.constraint(equalTo: uploadButton.leadingAnchor, constant: -8), textView.topAnchor.constraint(equalTo: topAnchor, constant: 8), textView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8), // 输入视图最小高度:确保初始状态有足够空间 heightAnchor.constraint(greaterThanOrEqualToConstant: 44) ] NSLayoutConstraint.activate(constraints) // 限制TextView最大高度(比如最多显示3行) textViewHeightConstraint = textView.heightAnchor.constraint(lessThanOrEqualToConstant: 132) textViewHeightConstraint?.isActive = true } @objc private func uploadTapped() { // 这里写上传逻辑:获取文本、发送到服务器等 guard let text = textView.text, !text.isEmpty else { return } print("上传文本:\(text)") textView.text = "" // 上传后清空文本 } // MARK: - KVO处理占位符 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "text" { let placeholderLabel = textView.subviews.first(where: { $0 is UILabel }) as? UILabel placeholderLabel?.isHidden = !textView.text.isEmpty } } }
二、监听键盘,让视图跟着上下移动
这部分的核心是监听键盘的弹出/收起通知,然后动态调整输入视图的底部约束。上面的setupNotifications方法可以这么写:
private func setupNotifications() { NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil ) } @objc private func keyboardWillShow(_ notification: Notification) { guard let userInfo = notification.userInfo, let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect, let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval, let curve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt else { return } // 计算键盘高度,要减去安全区域底部内边距(适配iPhone底部的Home Indicator) let keyboardHeight = keyboardFrame.height - superview?.safeAreaInsets.bottom ?? 0 // 获取并更新输入视图的底部约束(建议在ViewController里直接持有这个约束,更方便) if let parentVC = superview?.next as? UIViewController, let bottomConstraint = parentVC.view.constraints.first(where: { $0.firstItem as? UIView == self && $0.secondAnchor == parentVC.view.safeAreaLayoutGuide.bottomAnchor }) { bottomConstraint.constant = -keyboardHeight // 用和键盘一致的动画参数更新布局,过渡更自然 UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(rawValue: curve)) { parentVC.view.layoutIfNeeded() } } } @objc private func keyboardWillHide(_ notification: Notification) { guard let userInfo = notification.userInfo, let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval, let curve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt else { return } if let parentVC = superview?.next as? UIViewController, let bottomConstraint = parentVC.view.constraints.first(where: { $0.firstItem as? UIView == self && $0.secondAnchor == parentVC.view.safeAreaLayoutGuide.bottomAnchor }) { bottomConstraint.constant = 0 UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(rawValue: curve)) { parentVC.view.layoutIfNeeded() } } } deinit { NotificationCenter.default.removeObserver(self) textView.removeObserver(self, forKeyPath: "text") }
三、实现TextView的高度自适应
这部分靠TextView的代理方法textViewDidChange来实现,当文字变化时,自动计算需要的高度:
// MARK: - UITextViewDelegate func textViewDidChange(_ textView: UITextView) { // 计算文本内容需要的高度 let maxSize = CGSize(width: textView.frame.width, height: .infinity) let estimatedSize = textView.sizeThatFits(maxSize) // 因为我们设置了isScrollEnabled=false,TextView会自动根据内容撑高 // 触发布局更新,让输入视图跟着调整高度 layoutIfNeeded() }
四、ViewController里的整合
在ViewController中,把自定义输入视图添加到页面上,并设置初始约束:
class ChatViewController: UIViewController { private let inputView = CustomInputView() private var inputViewBottomConstraint: NSLayoutConstraint? override func viewDidLoad() { super.viewDidLoad() setupInputView() } private func setupInputView() { inputView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(inputView) inputViewBottomConstraint = inputView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) NSLayoutConstraint.activate([ inputView.leadingAnchor.constraint(equalTo: view.leadingAnchor), inputView.trailingAnchor.constraint(equalTo: view.trailingAnchor), inputViewBottomConstraint! ]) // 点击空白区域收起键盘 let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard)) view.addGestureRecognizer(tapGesture) } @objc private func hideKeyboard() { view.endEditing(true) } }
这样一来,你的自定义输入视图就完全符合需求了:初始在屏幕底部,点击TextView后键盘弹出,视图自动移到键盘上方;输入文字时TextView自动调整高度(最多3行),超过后会滚动;点击上传按钮可以处理文本上传。
内容的提问来源于stack exchange,提问作者Khledon
相关产品推荐
相关产品推荐

