对于RXSwift中的一些基本概念和说明请参看其他文章,接下来我们使用RXSwift一步一步去构建TableView,从简单到复杂。iOS开发过程中tableView的使用率是最高的,他的一些代理方法功能强大,添加起来也比较繁琐。今天就使用RXSwift来创建tableView
一.开始之前的准备
1.项目使用Xcode9开发,
2.新建项目,使用pod下载所需类库
pod 'RxSwift', '~> 3.6.1' #本次的主角
pod 'RxCocoa', '~> 3.6.1' #对RXSwift的一下拓展
pod 'NSObject+Rx' #NSObject的RX拓展
pod 'Alamofire', '~> 4.5.0' #网络请求
pod 'RxDataSources', '~> 1.0' #对tableview和Collection的一下拓展实现
pod 'Then', '~> 2.1.0'#简化对象初始化
pod 'Kingfisher', '~> 3.10.2' #网络图片缓存
大家可以根据自己的需求进行添加
3.直接使用storyBoard快速开始
二.下面开始开车了,请大家做好🤣🤣🤣( ̄▽ ̄)~*
在storyBoard中添加好tableView和对应的关联属性
tableView添加 cell的属性设置tableView在关联属性的时候没有设置代理,只是最简单的设置。在ViewController中引入
import RxSwift
import RxCocoa
下面直接贴上代码,再解释
class ZJSimpleTableViewController: UIViewController ,UITableViewDelegate{
@IBOutlet weak var myTableView: UITableView!
//在函数执行完之后就释放
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
// 创建一个sequence 可以发出多种事件信号,此地创建了数据源
let items = Observable.just(
(0..<20).map { "\($0)" }
)
//将数据源绑定到tableView的rx.items事件流中
items
.bind(to: myTableView.rx.items(cellIdentifier: "Cell", cellType: UITableViewCell.self)) { (row, element, cell) in
cell.textLabel?.text = "\(element) @ row \(row)"
}
.disposed(by: disposeBag)
//tableview的didSelected方法
myTableView.rx
.modelSelected(String.self)
.subscribe(onNext: { value in
print("开始选中====\(value)")
})
.disposed(by: disposeBag)
//tableviewcell中的点击感叹号
myTableView.rx
.itemAccessoryButtonTapped
.subscribe(onNext: { indexPath in
print("显示详情===\(indexPath.row)")
})
.disposed(by: disposeBag)
}
}
设置之后显示的tableview如下
simpleTableView.gif有没有很简单啊。设置代理,不需要,添加代理方法,不需要。看到这里你已经成功一小步了 。
三.添加带有Section的tableView
这下我们要引入
import RxDataSources
stroryBoard实现部分和上面的基本一样,在接下来的请求中只是修改了数据源,
//tableview绑定的数据源 定义数据源里面的数据类型 String :Double
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String,Double>>()
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
//创建数据源 每个sectionModel就是一个分组
let items = Observable.just([
SectionModel.init(model: "First Section", items: [
1.0,2.0,3.0]),
SectionModel.init(model: "Second Section", items: [
1.0,2.0,3.0]),
SectionModel.init(model: "Third Section", items: [
1.0,2.0,3.0])
])
dataSource.configureCell = {(_,tableView,indexPath,element) in
//注意此地的identifier的变化
let cell = tableView.dequeueReusableCell(withIdentifier: "sectionCell", for: indexPath) as UITableViewCell
cell.textLabel?.text = "显示的cell \(element) @ \(indexPath.row)"
return cell
}
//添加头
dataSource.titleForHeaderInSection = { data ,sectionIndex in
return data[sectionIndex].model
}
//绑定数据
items.bind(to: myTableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
//设置代理,可以设置header的高度和cell的高度
myTableView.rx.setDelegate(self).disposed(by: disposeBag)
//设置点击事件 包装信息 map函数把当前的indexPath和当前的数据包装传到subscribe函数中
myTableView.rx.itemSelected
.map{ [unowned self] indexPath in
return (indexPath,self.dataSource[indexPath])
}.subscribe(onNext: { (indexPath,element) in
print("当前选中==\(indexPath.row) @ \(element)")
}).disposed(by: disposeBag)
}
设置之后的tableView如下
sectionTableView.gif四.接下来创建一个可编辑的tableView,数据是从网上请求返回的。
先贴上效果图
editTableView.gif这个实现起来就有点绕了 ,请带好你的脑子🤣🤣🤣
这个是请求数据的显示信息,我们要先实现一个网络请求数据,新建一个swift文件,不继承任何类
import Foundation
import RxSwift
import struct Foundation.URL
import class Foundation.URLSession
func exampleError(_ error: String, location: String = "\(#file):\(#line)") -> NSError {
return NSError(domain: "ExampleError", code: -1, userInfo: [NSLocalizedDescriptionKey: "\(location): \(error)"])
}
class RandomUserAPI {
//定义一个单例
static let shareAPI = RandomUserAPI()
init() {}
//获取用户信息 URLSession
func getRandomUserResult() -> Observable<[Users]> {
let url = URL(string: "http://api.randomuser.me/?results=20")!
return URLSession.shared.rx.json(url: url).map{
json in
guard let json = json as? [String:AnyObject] else{
throw exampleError("不能转化成字典")
}
return try self.dealWithJSON(json: json)
}
}
//处理json的数据结果
func dealWithJSON(json:[String:AnyObject]) throws -> [Users] {
guard let result = json["results"] as? [[String:AnyObject]] else {
throw exampleError("找不到结果")
}
let userParseError = exampleError("结果处理出错")
let userResult:[Users] = try result.map { user in
let name = user["name"] as? [String:String]
let imageurl = user["picture"] as? [String:String]
guard let first = name?["first"] ,let last = name?["last"] ,let imageURL = imageurl?["large"] else{
throw userParseError
}
//添加USer类
let returnUser = Users(first: first, last: last, image: imageURL)
return returnUser
}
return userResult
}
}
接下来是USers类的实现,同样是新建一个swift文件,定义一个结构体(为什么是结构体我也不知道😂😂😂,swift中见过很多用结构体来定义Model的)
import Foundation
struct Users:Equatable,CustomDebugStringConvertible {
var firstName:String
var lastName:String
var imageURL:String
//初始化对象
init(first:String,last:String,image:String) {
firstName = first
lastName = last
imageURL = image
}
}
//CustomDebugStringConvertible 协议必须要实现的方法
extension Users{
var debugDescription:String{
return firstName + " " + lastName
}
}
//Equatable 协议必须要实现的方法
func ==(lhs: Users, rhs: Users) -> Bool {
return lhs.firstName == rhs.firstName &&
lhs.lastName == rhs.lastName &&
lhs.imageURL == rhs.imageURL
}
我们现在有数据源了,想着怎么把数据放进绑定的tableView中,还有tableView的一写操作中怎么处理操作和数据之间的关系
开始处理tableView的操作,新建文件定义宏
//定义tableView的操作
enum TableViewEditingCommand {
case setUsers(Users:[Users])//设置普通显示用户
case setFavoriteUsers(favoriteUsers:[Users])//设置喜欢显示用户数据
case deleteUser(IndexPath:IndexPath)//删除数据
case moveUser(from:IndexPath,to:IndexPath)//移动数据
}
接下来就是处理这四个事件所对应的方法
struct TableViewEditComandsViewModel {
let favoriteUsers:[Users]
let users :[Users]
//这里就是绑定各个枚举处理事件的方法,根据传进去的参数修改对应的数据
//添加方法
static func executeCommand(state:TableViewEditComandsViewModel,_ command:TableViewEditingCommand) -> TableViewEditComandsViewModel{
switch command {
case let .setUsers(Users):
return TableViewEditComandsViewModel.init(favoriteUsers: state.favoriteUsers, users: Users)
case let .setFavoriteUsers(favoriteUsers):
return TableViewEditComandsViewModel.init(favoriteUsers: favoriteUsers, users: state.users)
case let .deleteUser(IndexPath):
var all = [state.favoriteUsers,state.users]
all[IndexPath.section].remove(at: IndexPath.row)
return TableViewEditComandsViewModel.init(favoriteUsers: all[0], users: all[1])
case let .moveUser(from, to):
var all = [state.favoriteUsers,state.users]
let user = all[from.section][from.row]
all[from.section].remove(at: from.row)
all[to.section].insert(user, at: to.row)
return TableViewEditComandsViewModel.init(favoriteUsers: all[0], users: all[1])
}
}
}
然后就是数据的绑定和显示
对tableview的初始化数据抽出一个方法
static func configurationDataSource()-> RxTableViewSectionedReloadDataSource<SectionModel<String,Users>> {
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String,Users>>()
dataSource.configureCell = { (_,tv,ip,user:Users) in
let cell = tv.dequeueReusableCell(withIdentifier: "systemCell")!
cell.textLabel?.text = user.firstName + " " + user.lastName
return cell
}
//能否编辑
dataSource.titleForHeaderInSection = {
data,section in
return data[section].model
}
dataSource.canEditRowAtIndexPath = {(data,indexpath) in
return true
}
dataSource.canMoveRowAtIndexPath = { (data,indexpath) in
return true
}
return dataSource
}
viewController中定义
var isEdit: Bool = false //是否在编辑状态
let dataSource = ZJEditTableViewController.configurationDataSource()//tableView抽出的数据源方法
let disposeBag = DisposeBag()//Rx释放
我们将tableView操作命令和上面的枚举进行绑定
//初始化两个普通显示用户
let superMan = Users(
first: "Super",
last: "Man",
image: "http://nerdreactor.com/wp-content/uploads/2015/02/Superman1.jpg"
)
let watchMan = Users(
first:"Watch",
last:"Man",
image:"http://www.iri.upc.edu/files/project/98/main.GIF"
)
//请求数据
let loadFavoriteUsers = RandomUserAPI.shareAPI.getRandomUserResult()
//将原来的sequence转化为一个新的sequence
.map(TableViewEditingCommand.setUsers)
//错误处理信息 设置为一个空数组[]
.catchErrorJustReturn(TableViewEditingCommand.setUsers(Users: []))
//生成一个sequence 绑定superMan,watchMan ,concat会把多个sequence和并为一个sequence,并且当前面一个sequence发出了completed事件,才会开始下一个sequence的事件。
let initialLoadCommand = Observable.just(TableViewEditingCommand.setFavoriteUsers(favoriteUsers: [superMan,watchMan]))
.concat(loadFavoriteUsers)
.observeOn(MainScheduler.instance)
//绑定删除
let deleteUsersCommmand = myTableView.rx.itemDeleted.map(TableViewEditingCommand.deleteUser)
//绑定移动
let moveUserCommand = myTableView.rx.itemMoved.map(TableViewEditingCommand.moveUser)
//初始化信息 都为空
let initialState = TableViewEditComandsViewModel.init(favoriteUsers: [], users: [])
let viewModel = Observable.system(initialState,
accumulator: TableViewEditComandsViewModel.executeCommand,
scheduler: MainScheduler.instance,
feedback: {_ in initialLoadCommand},{_ in deleteUsersCommmand},{ _ in moveUserCommand})
.shareReplay(1)
//viewModel绑定到dataSource
viewModel
.map{[
SectionModel(model: "Favorite Users", items: $0.favoriteUsers),
SectionModel(model: "Normal Users", items: $0.users)
]}
.bind(to: myTableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
myTableView.rx.itemSelected
.map{ [unowned self] indexPath in
return (indexPath,self.dataSource[indexPath])
}.subscribe(onNext: { [unowned self](indexPath,user) in
self.showDetailsForUser(user)
}).disposed(by: disposeBag)
myTableView.rx.setDelegate(self)
.disposed(by: disposeBag)
添加headerView和设置header的高度
//添加headerSection
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let title = dataSource[section]
let label = UILabel(frame: CGRect.zero)
label.text = " \(title)"
label.textColor = UIColor.white
label.backgroundColor = UIColor.darkGray
label.alpha = 0.9
return label
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 40
}
@IBAction func tableViewEditAction(_ sender: Any) {
isEdit = !isEdit
myTableView.isEditing = isEdit
}
//具体用户信息的跳转界面
private func showDetailsForUser(_ user: Users) {
let storyboard = UIStoryboard(name: "EditTableView", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "userDetailVC") as! ZJUserDetailViewController
viewController.user = user
self.navigationController?.pushViewController(viewController, animated: true)
}
接下来就可以编译项目工程了
源码连接
以上是个人理解 ,如有错误信息欢迎指正😁😁😁😁
网友评论