iOS聊天App发送已读消息请求的最佳实现逻辑
iOS UITableView 聊天页已读消息上报最佳实践
你初步考虑的tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)方案可以实现基础功能,但存在明显的精度缺陷,不推荐作为核心实现逻辑,原因如下:
- 触发时机是cell即将进入视口,快速滑动列表时,用户根本没看清的掠过消息也会触发方法,导致大量误报
- 无法校验消息的实际停留时长,行业通用判定规则是消息完整出现在视口且停留超过300-500ms才算已读,这个方法做不到时长校验
- 列表reload、cell复用重绘时会重复触发,容易产生重复上报
推荐实现方案
目前主流即时通讯应用的已读上报都遵循「滚动停止后校验+停留时长判断+去重批量上报」的逻辑,具体实现步骤如下:
- 监听列表静止时机
不用在滚动过程中做已读判定,通过两个UIScrollView代理方法捕获列表完全静止的时刻:scrollViewDidEndDecelerating(_:):捕获列表惯性滚动停止的场景scrollViewDidEndDragging(_:willDecelerate:):捕获用户拖拽抬手后没有惯性滚动的场景
此外在页面首次加载完成、收到新消息自动滚到底部、页面即将消失、应用切后台时,手动触发一次已读检查做漏点补全。
- 校验真正完整可见的消息
列表静止后,不要直接使用tableView.indexPathsForVisibleRows的结果,遍历返回的indexPath,对每个indexPath获取对应cell,通过tableView.bounds.contains(cell.frame)判断cell是否完全落在tableView的可视区域内,过滤掉只露出边缘的消息。 - 加停留时长防抖
拿到完整可见的未上报消息列表后,不要立刻发请求,添加一个500ms的延迟任务,如果这500ms内列表再次发生滚动,就取消该延迟任务,等下次列表静止后重新校验,避免用户快速滑过时误判。 - 本地去重+批量上报
- 本地维护一个已上报消息ID的集合,每次上报前过滤掉已经报过的消息,避免重复请求
- 把本次满足条件的消息ID攒成批次,一次接口完成上报,不要单条消息触发一次请求,减少不必要的网络开销
如果是快速迭代的demo阶段,给
willDisplayCell逻辑加上本地去重也能凑合用,但只要正式上线对已读状态准确性有要求,就必须用上述方案,否则误报的已读状态会直接破坏聊天功能的核心体验。
内容的提问来源于stack exchange,提问作者ArisRS
相关产品推荐
相关产品推荐

