如何基于JSQMessageViewController创建新消息与自定义会话UI?
在JSQMessagesViewController中添加“To”收件人栏的实现指引
嘿,我刚好之前做过类似的需求,给你分享几个靠谱的实现方案,都是基于JSQ自带UI扩展的,不用完全重构现有界面:
方案一:自定义导航栏集成收件人栏
JSQ的导航栏默认只显示标题,我们可以把它替换成包含“To”标签和收件人展示/输入的自定义View,这样既贴合原有UI风格,又不破坏整体布局结构:
创建自定义收件人栏视图
先写一个RecipientBarView,包含“To:”标签和收件人展示/输入控件,示例代码如下:class RecipientBarView: UIView { let toLabel = UILabel() let recipientContent = UILabel() // 如果需要编辑可以换成UITextField override init(frame: CGRect) { super.init(frame: frame) setupUI() } required init?(coder: NSCoder) { super.init(coder: coder) setupUI() } private func setupUI() { backgroundColor = .systemBackground toLabel.text = "To:" toLabel.font = UIFont.systemFont(ofSize: 16, weight: .medium) recipientContent.font = UIFont.systemFont(ofSize: 16) recipientContent.textColor = .systemBlue // 用StackView做自适应布局 let stackView = UIStackView(arrangedSubviews: [toLabel, recipientContent]) stackView.spacing = 8 stackView.translatesAutoresizingMaskIntoConstraints = false addSubview(stackView) NSLayoutConstraint.activate([ stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16), stackView.centerYAnchor.constraint(equalTo: centerYAnchor), stackView.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor, constant: -16) ]) } // 提供更新收件人的方法 func updateRecipientName(_ name: String) { recipientContent.text = name } }替换JSQ的导航栏标题视图
在你的JSQMessagesViewController子类的viewDidLoad方法里,把导航栏的titleView换成我们的自定义视图:override func viewDidLoad() { super.viewDidLoad() // 初始化并配置收件人栏 let recipientBar = RecipientBarView(frame: CGRect(x: 0, y: 0, width: view.frame.width - 40, height: 44)) recipientBar.updateRecipientName("Sarah") // 替换成你的收件人数据 navigationItem.titleView = recipientBar // 其他JSQ基础配置 collectionView?.collectionViewLayout.incomingMessageViewAlignment = .left collectionView?.collectionViewLayout.outgoingMessageViewAlignment = .right inputToolbar.contentView.textView.placeholder = "输入消息..." }如果需要支持编辑收件人,把
recipientContent换成UITextField,再处理键盘弹出/收起的逻辑即可。
方案二:在消息列表顶部添加固定头部
如果不想修改导航栏,可以给JSQ的collectionView添加一个固定在顶部的头部视图作为“To”栏,它会跟着消息列表滚动但始终保持在可视区域顶部:
注册头部视图并配置布局
在viewDidLoad里完成头部视图的注册和布局设置:override func viewDidLoad() { super.viewDidLoad() // 注册自定义头部视图 collectionView?.register(RecipientHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "RecipientHeader") // 配置头部固定在顶部及高度 if let layout = collectionView?.collectionViewLayout as? JSQMessagesCollectionViewFlowLayout { layout.sectionHeadersPinToVisibleBounds = true layout.headerReferenceSize = CGSize(width: view.frame.width, height: 44) } }这里的
RecipientHeaderView可以复用方案一的UI布局,只需把父类改成UICollectionReusableView。实现头部视图的数据源方法
重写JSQ的collectionView(_:viewForSupplementaryElementOfKind:at:)方法,返回自定义头部:override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "RecipientHeader", for: indexPath) as! RecipientHeaderView header.updateRecipientName("John Doe") // 更新收件人信息 return header }
关键注意事项
- 避免输入工具栏重叠:如果用方案二,要调整
collectionView的内边距,防止消息列表被底部输入栏挡住:override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() if let layout = collectionView?.collectionViewLayout as? JSQMessagesCollectionViewFlowLayout { collectionView?.contentInset = UIEdgeInsets(top: 44, left: 0, bottom: layout.inputToolbarHeight, right: 0) } } - 适配深色模式:给自定义视图的文字、背景色设置动态颜色(比如
UIColor.label、UIColor.systemBackground),和JSQ原生UI保持风格统一。 - 数据同步:如果是从其他页面跳转过来,记得在初始化
JSQMessagesViewController子类时传入收件人数据,再调用更新方法刷新视图。
这样改造后,你的界面就会在JSQ原有UI基础上完美添加“To”栏,既符合原生风格,又满足功能需求啦!
内容的提问来源于stack exchange,提问作者Sarah
相关产品推荐
相关产品推荐

