UIKit学习笔记5-使用UITableView制作聊天页面

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要继承

    • UITableViewDataSource
    • UITableViewDelegate
    • UIViewController
  • 里面有两个默认函数

    • 1.用于数cell
    • 2.用于设置cell、写规则
  • 也可以设置其他函数

    • 函数1:实例化ChatViewController,并传值给该页面,再在点击对应行过去(使用self.navigationController.push...)
    Swift 复制代码
         func 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.分为上下留空栏,聊天气泡,头像,聊天文本等;
    • 2.采用MVC作法构建套件
    • 3.动态调节聊天内容高度及位置

Model

  • 创建一个模型,用于储存聊天信息
    • chatID(聊天框ID)
    • messageID(消息ID)
    • content(发送内容)
    • target(发送方的)
    • mineHead(我方头像)
    • otherHead(对方头像)
  • 创建一个数组
    • 参数为Model的数组
    • 遍历chatID来确定是和谁的聊天消息,返回对应的所有数据
    • 遍历chatID,将所有数据归类聊天信息
      • case"1", case"2",筛选chatID,返回不同的聊天记录

ChatTextCell

  • 继承自TableViewCell

  • 初始化重写

    • 重写初始化器
    • 调用父类的初始化器
    • 运行setupUI()
    Swift 复制代码
        override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?){
            super.init(style: style, reuseIdentifier: reuseIdentifier)
            setupUI()
        }
    • 可失败初始化器
    Swift 复制代码
     required init?(coder: NSCoder){//必须的初始化流程
            fatalError("init(coder:) has not been implemented")
  • setupUI

    • 用于添加里面的组件
  • 创建组件,即为TextCell的组成部分

    • userHead(用户头像)

      • 头像图片,大小固定,位置不固定,设zero
      • 圆角
      Swift 复制代码
          lazy 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(都不固定)
      • 圆角
      Swift 复制代码
        lazy 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
      • 字体颜色
      • 字号
      Swift 复制代码
          lazy 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,计算文字框的大小和位置
    • 计算位置
    • 计算宽度,宽度不足,为保证一个最大宽度,设置一个数值
    Swift 复制代码
     let 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的值
    Swift 复制代码
    contentLabel.frame = .init(origin: .init(x: 8, y: 8), size: str_size.size)
            //设置文本标签的内容
    contentLabel.text = chatModel.content
            //计算文本背景的大小和位置
    定义另一个函数border_size,计算背景的大小(即背景视图)
    同时也需要target,使用if else来判断是哪方的消息
    • "other"

      • 头像位置
      • 头像图片
      • 背景大小
      • 背景颜色
    • "mine"(else)

      • 头像位置
      • 头像图片
      • 背景大小
      • 背景颜色
      Swift 复制代码
              if 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

  • 继承自
    • UIViewController
    • UITableViewDelegate
    • UITableViewDataSource
  • 创建一个变量,用于接收消息栏页面传过来的数据,也就是判断与谁聊天
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

    Swift 复制代码
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            chatModels.count
        }

    2.用于设置并渲染cell

    创建一个cell常量

    Swift 复制代码
     let cell: ChatTextCell = tableView.cellForRow(at: indexPath) as? ChatTextCell ?? ChatTextCell(style: .default, reuseIdentifier: "cell")

    调用tableViewcellForRow(at:)方法,并用indexPath获取其位置,将其转换为ChatTextCell类型。

    如果转换失败,则创建一个新的ChatTextCell实例,使用 .default 作为样式,"cell" 作为重用标识符。

    创建常量:根据索引取得目标cell

    Swift 复制代码
    let model = chatModels[indexPath.row]

    渲染cell

    Swift 复制代码
            cell.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)

相关推荐
Alice-YUE2 小时前
【js高频八股】防抖与节流
开发语言·前端·javascript·笔记·学习·ecmascript
北山有鸟3 小时前
修改源码法和插件法
嵌入式硬件·学习
richxu202510013 小时前
嵌入式学习之路->stm32篇->(14)通用定时器(上)
stm32·单片机·嵌入式硬件·学习
小陈phd3 小时前
TensorRT 入门完全指南(一)——从核心定义到生态工具全解析
人工智能·笔记
朗清风3 小时前
“\“在字符串表示正则语义中的作用
swift
是上好佳佳佳呀3 小时前
【前端(十一)】JavaScript 语法基础笔记(多语言对比)
前端·javascript·笔记
qeen873 小时前
【数据结构】建堆的时间复杂度讨论与TOP-K问题
c语言·数据结构·c++·学习·
handler014 小时前
Linux 内核剖析:进程优先级、上下文切换与 O(1) 调度算法
linux·运维·c语言·开发语言·c++·笔记·算法
lizhihai_994 小时前
股市学习心得-六张分时保命图
大数据·人工智能·学习