對於RXSwift中的一些基本概念和說明請參看其餘文章,接下來咱們使用RXSwift一步一步去構建TableView,從簡單到複雜。iOS開發過程當中tableView的使用率是最高的,他的一些代理方法功能強大,添加起來也比較繁瑣。今天就使用RXSwift來建立tableViewreact
1.項目使用Xcode9開發,
2.新建項目,使用pod下載所需類庫
git
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快速開始github
在storyBoard中添加好tableView和對應的關聯屬性json
tableView在關聯屬性的時候沒有設置代理,只是最簡單的設置。在ViewController中引入swift
import RxSwift import RxCocoa
下面直接貼上代碼,再解釋api
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以下數組
有沒有很簡單啊。設置代理,不須要,添加代理方法,不須要。看到這裏你已經成功一小步了 。緩存
這下咱們要引入網絡
import RxDataSources
stroryBoard實現部分和上面的基本同樣,在接下來的請求中只是修改了數據源,app
//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以下
先貼上效果圖
這個實現起來就有點繞了 ,請帶好你的腦子🤣🤣🤣
這個是請求數據的顯示信息,咱們要先實現一個網絡請求數據,新建一個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) }
以上是我的理解 ,若有錯誤信息歡迎指正😁😁😁😁