用 RxSwift 实现 UITableView 的响应式绑定(超实用示例)

目录

前言

一、环境准备

1.安装 RxSwift 和 RxCocoa

2.导入模块

二、实现一个简单的UITableView

1.实现一个简单的 UITableView

1.实现步骤

1.我们声明一个ViewModel

2.ViewModel和UITableView 绑定

2.实现 UITableView 的代理方法

三、处理点击事件


前言

        在 iOS 开发中,UITableView 是我们最常使用的组件之一。传统的写法需要设置 delegate、datasource,实现多个方法。而使用 RxSwift + RxCocoa,我们可以让列表渲染逻辑更清晰、响应式,更符合 MVVM 模式。

        这篇文章将手把手教你用 RxSwift 来绑定 UITableView,构建一个“设置”页面,包括多个 section 和点击事件。

一、环境准备

1.安装 RxSwift 和 RxCocoa

        首先我们要安装 RxSwift 和 RxCocoa。你可以选择你喜欢的方式,我这里以 cocoapods 为例:

        这里我使用的Xcode 的版本为Xcode 16.3,RxSwift 和 RxCocoa的版本号为 6.9.0。

pod 'RxSwift'
pod 'RxCocoa'

2.导入模块

import RxSwift
import RxCocoa

二、实现一个简单的UITableView

        我们以一个简单的 UITableView 为例,看看如何实现一个最简单的 UITableView。具体要实现的 UI 效果如图所示:

图 1.简单的 UITableView

1.实现一个简单的 UITableView

1.实现步骤

        我们看一下详细的实现步骤:

1.我们声明一个ViewModel

class SimpleTableViewModel {

    // 模拟数据源

    let items = Observable.just([

        "第一行", "第二行", "第三行", "第四行", "第五行"

    ])

}

2.ViewModel和UITableView 绑定

        设置好数据之后,我们调用 bind 方法把 UITableView 和 ViewModel 绑定即可实现一个简单的 UITableView。

    private func setupBindingUITableView(){

        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")

        tableView.dataSource = nil

        tableView.delegate = nil

        // 绑定 ViewModel 数据到 tableView

        viewModel.items

            .bind(to: tableView.rx.items(cellIdentifier: "Cell")) { index, model, cell in

                cell.textLabel?.text = "\(index + 1).\(model)"

                cell.accessoryType = .disclosureIndicator

            }

            .disposed(by: disposeBag)

    }

        完整的代码如下:

import UIKit
import RxSwift
import RxCocoaclass SimpleTableViewModel {// 模拟数据源let items = Observable.just(["第一行", "第二行", "第三行", "第四行", "第五行"])
}class SimpleTableViewController: UITableViewController {private let disposeBag = DisposeBag()private let viewModel = SimpleTableViewModel()override func viewDidLoad() {super.viewDidLoad()self.title = "Rx 简单列表"setupBindingUITableView()}private func setupBindingUITableView(){tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")tableView.dataSource = niltableView.delegate = nil// 绑定 ViewModel 数据到 tableViewviewModel.items.bind(to: tableView.rx.items(cellIdentifier: "Cell")) { index, model, cell incell.textLabel?.text = "\(index + 1).\(model)"cell.accessoryType = .disclosureIndicator}.disposed(by: disposeBag)}
}

2.实现 UITableView 的代理方法

        RxCocoa 还封装了UITableView的DataSource方法,例如我们想要实现 cell的点击方法,仅需要调用下面的代码即可:

        tableView.rx.itemSelected.subscribe {[weak self] indexPath in

            debugPrint("点击了 Section: \(indexPath.section), Row: \(indexPath.row)")

        }.disposed(by: disposeBag)

        除此之外,封装了 itemDeselected(反选)、itemHighlighted(高亮)、itemUnhighlighted(取消高亮)、itemAccessoryButtonTapped(itemAccessory 点击)、itemInserted(item 增加)、itemDeleted(item 删除)等方法,这里您可以调用下看看。

        这里我写了一个简单的 UITableView 数据源的增加和删除功能 demo,完整代码如下:

import UIKit
import SnapKit
import RxSwift
import RxCocoaclass RxSwiftTableViewModel {// 模拟数据源var items = BehaviorSubject(value: ["第一行", "第二行", "第三行", "第四行", "第五行"])// 增加一行数据func addItem() {var currentItems = try! items.value()currentItems.append("新的一行")items.onNext(currentItems)}// 删除一行数据func removeItem(at index: Int) {var currentItems = try! items.value()if index >= 0 && index < currentItems.count {currentItems.remove(at: index)items.onNext(currentItems)}}}class RxSwiftUITableViewDemosVC: UIViewController {private let disposeBag = DisposeBag()private let viewModel = RxSwiftTableViewModel()private let tableView = UITableView()private let addButton = UIButton(type: .system)private let removeButton = UIButton(type: .system)override func viewDidLoad() {super.viewDidLoad()self.title = "RxSwift UITableView Demo"view.backgroundColor = .white// 使用 SnapKit 设置 tableView 布局setupTableView()// 使用 SnapKit 设置按钮布局setupButtons()// 绑定 ViewModel 数据到 tableViewviewModel.items.bind(to: tableView.rx.items(cellIdentifier: "Cell")) { index, model, cell incell.textLabel?.text = model}.disposed(by: disposeBag)// 处理点击事件tableView.rx.itemSelected.subscribe(onNext: { [weak self] indexPath inself?.tableView.deselectRow(at: indexPath, animated: true)print("点击了 Section: \(indexPath.section), Row: \(indexPath.row)")}).disposed(by: disposeBag)// 增加数据按钮点击事件addButton.rx.tap.subscribe(onNext: { [weak self] inself?.viewModel.addItem()}).disposed(by: disposeBag)// 删除数据按钮点击事件removeButton.rx.tap.subscribe(onNext: { [weak self] in// 删除最后一行数据let currentCount = (try? self?.viewModel.items.value().count ?? 0) ?? 0let lastIndex = currentCount - 1if lastIndex >= 0 {self?.viewModel.removeItem(at: lastIndex)}}).disposed(by: disposeBag)//选中 modeltableView.rx.modelSelected(String.self).subscribe { element indebugPrint("you selected section:\(element)")}.disposed(by: disposeBag)}private func setupTableView() {// 添加 tableView 到视图view.addSubview(tableView)// 使用 SnapKit 设置 tableView 布局tableView.snp.makeConstraints { make inmake.top.equalToSuperview()make.left.right.equalToSuperview()make.bottom.equalToSuperview().offset(-100) // 留出按钮位置}// 注册 UITableViewCelltableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")}private func setupButtons() {// 添加按钮到视图view.addSubview(addButton)view.addSubview(removeButton)// 设置按钮标题addButton.setTitle("增加一行数据", for: .normal)removeButton.setTitle("删除一行数据", for: .normal)// 使用 SnapKit 设置按钮布局addButton.snp.makeConstraints { make inmake.left.equalToSuperview().inset(20)make.bottom.equalToSuperview().inset(20)make.height.equalTo(44)make.width.equalToSuperview().dividedBy(2).offset(-30)}removeButton.snp.makeConstraints { make inmake.right.equalToSuperview().inset(20)make.bottom.equalToSuperview().inset(20)make.height.equalTo(44)make.width.equalToSuperview().dividedBy(2).offset(-30)}}
}

三、处理点击事件

        当你需要实现更复杂的 UITableView,可以实用 RxSwift+RxDataSource 实现更复杂的 UITableView。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/81415.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】通过红黑树封装map和set

前言&#xff1a; 通过之前的学习&#xff0c;我们已经学会了红黑树和map、set。这次我们要实现自己的map和set&#xff0c;对&#xff0c;使用红黑树进行封装&#xff01; 当然&#xff0c;红黑树内容这里就不在赘述&#xff0c;我们会复用红黑树的代码&#xff0c;所以先将…

非凸科技受邀出席AI SPARK活动,共探生成式AI驱动金融新生态

4月19日&#xff0c;由AI SPARK社区主办的“生成式AI创新与应用构建”主题沙龙在北京举行。活动聚焦生成式AI的技术突破与产业融合&#xff0c;围绕大模型优化、多模态应用、存内计算等前沿议题展开深度探讨。非凸科技受邀出席并发表主题演讲&#xff0c;深入解析金融垂直大模型…

【Java IO流】IO流详解

参考笔记&#xff1a;【Java基础-3】吃透Java IO&#xff1a;字节流、字符流、缓冲流_javaio-CSDN博客 目录 1.IO流简介 1.1 什么是IO流&#xff1f; 1.2 IO流的分类 1.3 字符流和字节流的其他区别 1.4 Java IO流体系图 2.字符编码详解 3. Java的char类型与 Unicode、U…

驱动开发系列56 - Linux Graphics QXL显卡驱动代码分析(三)显示模式设置

一:概述 如之前介绍,在qxl_pci_probe 中会调用 qxl_modeset_init 来初始化屏幕分辨率和刷新率,本文详细看下 qxl_modeset_init 的实现过程。即QXL设备的显示模式设置,是如何配置CRTC,Encoder,Connector 的以及创建和更新帧缓冲区的。 二:qxl_modeset_init 分析 in…

Vue3开发常见性能问题知多少

文章目录 1 常见性能优化瓶颈及原因1.1 响应式数据的过度使用1.2 虚拟 DOM 的频繁更新1.3 组件渲染的冗余1.4 大列表渲染的性能问题1.5 计算属性和侦听器的滥用1.6 事件处理函数的频繁绑定1.7 异步组件的加载性能2 解决方案与优化技巧2.1 合理使用响应式数据2.2 优化虚拟 DOM 更…

Rust Ubuntu下编译生成环境win程序踩坑指南

前言&#xff1a; 1&#xff0c;公司要给一线搞一个升级程序&#xff0c;需要在win下跑。 之前都是找开发总监帮忙&#xff0c;但是他最近比较忙。就让我自己搞。有了下文.。说来惭愧&#xff0c;之前写过一篇ubuntu下编译windows的文章。里面的demo就一句话 fuck world。依赖…

openharmony 4.1 运行busybox工具包(保姆教程)

1.下载 链接&#xff1a;Index of /downloads/binaries 进入其中后&#xff0c;找到 挑选适合你系统架构的版本&#xff0c;例如我这边是 https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-armv7r 右键复制链接 打开迅雷&#xff0c;直接粘…

算法四 习题 1.3

数组实现栈 #include <iostream> #include <vector> #include <stdexcept> using namespace std;class MyStack { private:vector<int> data; // 用于存储栈元素的数组public:// 构造函数MyStack() {}// 入栈操作void push(int val) {data.push_back…

GD32F407单片机开发入门(十七)内部RTC实时时钟及实战含源码

文章目录 一.概要二.RTC基本特点三.GD32单片机RTC内部结构图四.配置一个RTC走秒例程五.工程源代码下载六.小结 一.概要 RTC&#xff08;Real-Time Clock&#xff09;是一种用于追踪和记录实际时间的时钟系统。RTC模块提供了一个包含日期&#xff08;年/月/日&#xff09;和时间…

新能源汽车运动控制器核心芯片选型与优化:MCU、DCDC与CANFD协同设计

摘要&#xff1a;随着新能源汽车产业的迅猛发展&#xff0c;汽车运动控制器的性能和可靠性面临着更高的要求。本文深入探讨了新能源汽车运动控制器中MCU&#xff08;微控制单元&#xff09;、DCDC电源管理芯片和CANFD总线通信芯片的选型要点、优化策略及其协同设计方案。通过综…

2.maven 手动安装 jar包

1.背景 有的时候&#xff0c;maven仓库无法下载&#xff0c;可以手动安装。本文以pentaho-aggdesigner-algorithm-5.1.5-jhyde.jar为例。 2.预先准备 下载文件到本地指定位置。 2.1.安装pom mvn install:install-file \-Dfile/home/wind/tmp/pentaho-aggdesigner-5.1.5-jh…

OpenCV 图形API(75)图像与通道拼接函数-----将 4 个单通道图像矩阵 (GMat) 合并为一个 4 通道的多通道图像矩阵函数merge4()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 由4个单通道矩阵创建一个4通道矩阵。 该函数将多个矩阵合并为一个单一的多通道矩阵。也就是说&#xff0c;输出矩阵的每一个元素都是输入矩阵对…

AI日报 · 2025年05月02日 | 再见GPT-4!OpenAI CEO 确认 GPT-4 已从 ChatGPT 界面正式移除

1、OpenAI CEO 确认 GPT-4 已从 ChatGPT 界面正式移除 在处理 GPT-4o 更新问题的同时&#xff0c;OpenAI CEO Sam Altman 于 5 月 1 日在 X 平台发文&#xff0c;正式确认初代 GPT-4 模型已从 ChatGPT 主用户界面中移除。此举遵循了 OpenAI 此前公布的计划&#xff0c;即在 4 …

patch命令在代码管理中的应用

patch 是一个用于将差异文件&#xff08;补丁&#xff09;应用到源代码的工具&#xff0c;常用于修复 bug、添加功能或调整代码结构。在您提供的代码中&#xff0c;patch 命令通过一系列补丁文件&#xff08;.patch&#xff09;修改了 open-amp 库的源代码。 patch 命令的核心作…

spring-ai集成langfuse

1、pom文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.…

PyTorch 与 TensorFlow:深度学习框架的深度剖析与实战对比

PyTorch 与 TensorFlow&#xff1a;深度学习框架的深度剖析与实战对比 摘要 &#xff1a;本文深入对比 PyTorch 与 TensorFlow 两大深度学习框架&#xff0c;从核心架构、优缺点、适用场景等多维度剖析&#xff0c;结合实例讲解&#xff0c;帮助开发者清晰理解两者特性&#x…

如何配置NGINX作为反向代理服务器来缓存后端服务的响应?

大家好&#xff0c;我是锋哥。今天分享关于【如何配置NGINX作为反向代理服务器来缓存后端服务的响应&#xff1f;】面试题。希望对大家有帮助&#xff1b; 如何配置NGINX作为反向代理服务器来缓存后端服务的响应&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源…

DiT:文档图像Transformer 的自监督预训练

摘要 图像transformer&#xff08;Image Transformer&#xff09;最近在自然图像理解方面取得了显著进展&#xff0c; 无论是使用监督&#xff08;ViT、DeiT等&#xff09;还是自监督&#xff08;BEiT、MAE等&#xff09;预训练技术。在本文中&#xff0c;我们提出了DiT&#…

51c嵌入式~电路~合集4

我自己的原文哦~ https://blog.51cto.com/whaosoft/11888986 一、电流检测电路 电流检测的应用 电路检测电路常用于&#xff1a;高压短路保护、电机控制、DC/DC换流器、系统功耗管理、二次电池的电流管理、蓄电池管理等电流检测等场景。电路专辑 对于大部分应用&#xff…

【Git】万字详解 Git 的原理与使用(上)

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 1. 初识 Git1.1 Git 是什么&#xff1f;1.2 为什么要有 Git 2. 安装 Git2.1 Linux-Ubuntu 安装 Git2.2 Windo…