UITableView制作聊天页面

创建消息栏页面

- 思路:使用MVC建构及原型
这个页面要用到滚动视图**UITableView,但是需要滚动的Cell的宽高、位置并不是固定的。**
里面的Label的宽高和位置也不是固定的。
通过Public的**getStrSize函数来获取文本的尺寸,这是一个固定的大小。**
我们需要一个新的页面(ViewController),然后一样用MVC架构来写,同时使用UITableView,所以也需要对应的Cell和Model。
先修改一下管理信息页面的MessCellModel,添加chatID元素来管理这是第几个聊天框,决定要跳转的页面。
Model(Struct)
- 创建一个模型,为储存信息
chatID(聊天框ID)headingname(头像)nameText(联系人昵称)timeText(聊天时间)reviewText(聊天预览)
- 创建一个数组,里面装一个个模型
- 保存数据供以后读取
Cell
- 继承自
UITableViewCell - 初始化重写
- ①重写父类初始化器
- ②使用super
- 调用
setupUI- 用于把组件组合起来
setupUI用于addSubView- 创建组件,即为Cell的组成部分
ViewController(管理界面的页面)
-
使用UITableView的ViewController要继承
UITableViewDataSourceUITableViewDelegateUIViewController
-
里面有两个默认函数
- 1.用于数cell
- 2.用于设置cell、写规则
-
也可以设置其他函数
- 函数1:实例化
ChatViewController,并传值给该页面,再在点击对应行过去(使用self.navigationController.push...)
Swiftfunc tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let messCellModel = messCellModels[indexPath.row]//通过row索引,获取数组里的值 let chatVC = ChatViewController()//创建另一页的实例 chatVC.messCellModel = messCellModel //用一个变量来接收 self.navigationController?.pushViewController(chatVC, animated: true)//跳转到这个实例页 }- 输入height的函数2:用于设置Cell的高度(固定高度)
- 函数1:实例化
-
创建滚动视图,并将滚动视图添加到页面
创建聊天页
- 思路:
- 1.分为上下留空栏,聊天气泡,头像,聊天文本等;
- 2.采用MVC作法构建套件
- 3.动态调节聊天内容高度及位置
Model
- 创建一个模型,用于储存聊天信息
chatID(聊天框ID)messageID(消息ID)content(发送内容)target(发送方的)mineHead(我方头像)otherHead(对方头像)
- 创建一个数组
- 参数为Model的数组
- 遍历chatID来确定是和谁的聊天消息,返回对应的所有数据
- 遍历chatID,将所有数据归类聊天信息
- case"1", case"2",筛选chatID,返回不同的聊天记录
ChatTextCell
-
继承自
TableViewCell -
初始化重写
- 重写初始化器
- 调用父类的初始化器
- 运行setupUI()
Swiftoverride init(style: UITableViewCell.CellStyle, reuseIdentifier: String?){ super.init(style: style, reuseIdentifier: reuseIdentifier) setupUI() }- 可失败初始化器
Swiftrequired init?(coder: NSCoder){//必须的初始化流程 fatalError("init(coder:) has not been implemented") -
setupUI- 用于添加里面的组件
-
创建组件,即为TextCell的组成部分
-
userHead(用户头像)- 头像图片,大小固定,位置不固定,设zero
- 圆角
Swiftlazy var userHead: UIImageView = { var userHead = UIImageView(frame: .init(origin: .zero, size: .init(width: 30, height: 30)))//头像大小设置为30,会变所以用var了 userHead.layer.cornerRadius = 15 userHead.layer.masksToBounds = true return userHead }() -
boader_view(聊天框背景)- 背景色
- 大小和位置都设zero(都不固定)
- 圆角
Swiftlazy var boader_view: UIView = { var boader_view = UIImageView(frame: .zero)//大小和位置都不定! boader_view.layer.cornerRadius = 8//设置圆角 boader_view.layer.masksToBounds = true//切掉 return boader_view }() -
contentLabel(聊天内容)- 背景色:透明
- 大小和位置都不确定,设zero
- 字体颜色
- 字号
Swiftlazy var contentLabel: UILabel = { var contentLabel = UILabel(frame: .zero) contentLabel.font = UIFont.systemFont(ofSize: 16) contentLabel.backgroundColor = .clear//文本框设置为透明 contentLabel.numberOfLines = 0 contentLabel.textColor = .black return contentLabel }(
-
-
结合设置与上述组件
定义一个常量
str_size,计算文字框的大小和位置- 计算位置

- 计算宽度,宽度不足,为保证一个最大宽度,设置一个数值
Swiftlet string_size = Public.getStrSize(text: chatModel.content, font: .systemFont(ofSize: 16), size: .init(width: UIScreen.main.bounds.width - (8 + 30 + 8), height: 200))8是头像与边缘距离,30是头像宽度,8是与头像与文本框的距离。16是 文本与文本框的框内的间距:8乘2。
由于height不定,就先为文本指定最大height
- 设置文本框
contentLabel的位置 - 设置文本框
contentLabel的值
SwiftcontentLabel.frame = .init(origin: .init(x: 8, y: 8), size: str_size.size) //设置文本标签的内容 contentLabel.text = chatModel.content //计算文本背景的大小和位置定义另一个函数border_size,计算背景的大小(即背景视图)

同时也需要target,使用if else来判断是哪方的消息
-
"other"
- 头像位置
- 头像图片
- 背景大小
- 背景颜色
-
"mine"(else)
- 头像位置
- 头像图片
- 背景大小
- 背景颜色
Swiftif chatModel.target == "other" { userHead.frame = .init(origin: .init(x: 8, y: 0), size: userHead.frame.size ) //对方发的,头像在左边,所以位置是x: 8,尺寸是固定的 userHead.image = UIImage(named: chatModel.otherHead) //计算背景的大小和位置 boader_view.frame = .init(origin: .init(x: 8 + 30 + 8, y: 0), size: border_size) //设置两边背景的颜色 boader_view.backgroundColor = .white } else { userHead.frame = .init(origin: .init(x: ChatTextCell.chatTextCellWidth - (8 + 30), y: 0), size: userHead.frame.size) userHead.image = UIImage(named: chatModel.mineHead) boader_view.frame = .init(origin: .init(x: ChatTextCell.chatTextCellWidth - 8 - 30 - 8 - border_size.width, y: 0), size: border_size) boader_view.backgroundColor = .systemGreen } }
ChatViewController
- 继承自
UIViewControllerUITableViewDelegateUITableViewDataSource
- 创建一个变量,用于接收消息栏页面传过来的数据,也就是判断与谁聊天
Swift
//这是另一个页面
var messCellModel: MessCellModel!
- 创建一个变量,用于把Model的数据赋予过来,也就是数据源,后面要使用它,也要数cell。
Swift
var chatModels: [ChatModel] = []
- 因为要用到后面一系列操作,所以这个地方还不能给它传值,先定义一下
chatModels
这个地方要放在全局上,但是不能放在赋值这里。因为整个类还没初始化,初始化完成之后messCellModel肯定有值了,就可以用messCellModel里面的chatID去找是哪个聊天框。
Swift
override func viewDidLoad() {
super.viewDidLoad()
chatModels = ChatModel.getMessage(with: messCellmodel.chatID)
self.view.addSubview(chatScroll)
self.view.backgroundColor = .white
}
-
有两个默认函数
1.用于数cell
Swiftfunc tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { chatModels.count }2.用于设置并渲染cell
创建一个cell常量
Swiftlet cell: ChatTextCell = tableView.cellForRow(at: indexPath) as? ChatTextCell ?? ChatTextCell(style: .default, reuseIdentifier: "cell")调用
tableView的cellForRow(at:)方法,并用indexPath获取其位置,将其转换为ChatTextCell类型。如果转换失败,则创建一个新的ChatTextCell实例,使用
.default作为样式,"cell"作为重用标识符。创建常量:根据索引取得目标cell
Swiftlet model = chatModels[indexPath.row]渲染cell
Swiftcell.redrawThisView(with: model) //由于在Cell页面已经写好了Cell的属性函数,直接调用就好 cell.backgroundColor = .clear //Cell的背景设置为透明 cell.selectionStyle = .none //Cell的选中效果去除 return cell -
其他函数
打height就能出来的相关函数。它是TableViewControl里用于设置Cell之间的高度,这个函数返回的一定是height
-
因为Cell高度需要自适应,必须计算
-
返回重写的cell的高度
-
为了更稳妥,行高计算时要使用**
UIScreen.main.bounds.width**
Swift
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let chatModel = chatModels[indexPath.row]
let size = Public.getStrSize(text: chatModel.content, font: .systemFont(ofSize: 16), size: .init(width: UIScreen.main.bounds.width - (8 + 30 + 8) * 2 - 16, height: 200))
let border_size = CGSize(width: size.width + 16, height: size.height + 16)
return border_size.height + 12
}
创建滚动视图,并将其添加到页面
Swift
lazy var chatScroll: UITableView = {
let chatScroll = UITableView(frame: .init(x: 0, y: 64, width: self.view.frame.width, height: self.view.frame.height - 64 - 64))
//↓这两行是必须的,固定的
chatScroll.delegate = self
chatScroll.dataSource = self
chatScroll.separatorStyle = .none
chatScroll.backgroundColor = .cyan
return chatScroll
}()
Public
- 用于通过文本内容和字号,来计算其大小的函数
Swift
static func getStrSize(text: String, font: UIFont, size: CGSize) -> CGRect {
let strAtt = [NSAttributedString.Key.font : font]
let needOption = NSStringDrawingOptions.usesLineFragmentOrigin
let rect: CGRect = text.boundingRect(with: size, options: needOption, attributes: strAtt, context: nil)
return rect
}
}
设置属性
strAtt: 创建一个字典,将字体设置为字符串的属性,使用 NSAttributedString.Key.font 键
绘制
needOption: 指定字符串绘制的选项,usesLineFragmentOrigin 表示计算矩形时考虑完整的行(即字符串可能分为多行)
计算矩形区域
boundingRect(with: options: attributes: context:): 这是 NSString 的方法,用于计算字符串在给定约束(宽度和高度)内所需的最小矩形区域。
- with: size: 最大允许的宽度和高度。
- options: 绘制的选项(此处已将行段考虑在内)。
- attributes: 字符串的属性(字体)。
- context : 通常为
nil,表示不需要上下文信息。
最后返回rect(其实就是CGRect)