RxSwift/RxExample/RxExample/Examples/TableViewWithEditingCommands/TableViewWithEditingCommand...

178 lines
6.3 KiB
Swift

//
// TableViewWithEditingCommandsViewController.swift
// RxExample
//
// Created by carlos on 26/5/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import UIKit
#if !RX_NO_MODULE
import RxSwift
import RxCocoa
#endif
/**
Another way to do "MVVM". There are different ideas what does MVVM mean depending on your background.
It's kind of similar like FRP.
In the end, it doesn't really matter what jargon are you using.
This would be the ideal case, but it's really hard to model complex views this way
because it's not possible to observe partial model changes.
*/
struct TableViewEditingCommandsViewModel {
let favoriteUsers: [User]
let users: [User]
func executeCommand(_ command: TableViewEditingCommand) -> TableViewEditingCommandsViewModel {
switch command {
case let .setUsers(users):
return TableViewEditingCommandsViewModel(favoriteUsers: favoriteUsers, users: users)
case let .setFavoriteUsers(favoriteUsers):
return TableViewEditingCommandsViewModel(favoriteUsers: favoriteUsers, users: users)
case let .deleteUser(indexPath):
var all = [self.favoriteUsers, self.users]
all[(indexPath as NSIndexPath).section].remove(at: (indexPath as NSIndexPath).row)
return TableViewEditingCommandsViewModel(favoriteUsers: all[0], users: all[1])
case let .moveUser(from, to):
var all = [self.favoriteUsers, self.users]
let user = all[(from as NSIndexPath).section][(from as NSIndexPath).row]
all[(from as NSIndexPath).section].remove(at: (from as NSIndexPath).row)
all[(to as NSIndexPath).section].insert(user, at: (to as NSIndexPath).row)
return TableViewEditingCommandsViewModel(favoriteUsers: all[0], users: all[1])
}
}
}
enum TableViewEditingCommand {
case setUsers(users: [User])
case setFavoriteUsers(favoriteUsers: [User])
case deleteUser(indexPath: IndexPath)
case moveUser(from: IndexPath, to: IndexPath)
}
class TableViewWithEditingCommandsViewController: ViewController, UITableViewDelegate {
@IBOutlet weak var tableView: UITableView!
let dataSource = TableViewWithEditingCommandsViewController.configureDataSource()
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.rightBarButtonItem = self.editButtonItem()
let superMan = User(
firstName: "Super",
lastName: "Man",
imageURL: "http://nerdreactor.com/wp-content/uploads/2015/02/Superman1.jpg"
)
let watMan = User(firstName: "Wat",
lastName: "Man",
imageURL: "http://www.iri.upc.edu/files/project/98/main.GIF"
)
let loadFavoriteUsers = RandomUserAPI.sharedAPI
.getExampleUserResultSet()
.map(TableViewEditingCommand.setUsers)
let initialLoadCommand = Observable.just(TableViewEditingCommand.setFavoriteUsers(favoriteUsers: [superMan, watMan]))
.concat(loadFavoriteUsers)
.observeOn(MainScheduler.instance)
let deleteUserCommand = tableView.rx_itemDeleted.map(TableViewEditingCommand.deleteUser)
let moveUserCommand = tableView.rx_itemMoved.map(TableViewEditingCommand.moveUser)
let initialState = TableViewEditingCommandsViewModel(favoriteUsers: [], users: [])
let viewModel = Observable.of(initialLoadCommand, deleteUserCommand, moveUserCommand)
.merge()
.scan(initialState) { $0.executeCommand($1) }
.shareReplay(1)
viewModel
.map {
[
SectionModel(model: "Favorite Users", items: $0.favoriteUsers),
SectionModel(model: "Normal Users", items: $0.users)
]
}
.bindTo(tableView.rx_itemsWithDataSource(dataSource))
.addDisposableTo(disposeBag)
tableView.rx_itemSelected
.withLatestFrom(viewModel) { i, viewModel in
let all = [viewModel.favoriteUsers, viewModel.users]
return all[i.section][i.row]
}
.subscribeNext { [weak self] user in
self?.showDetailsForUser(user)
}
.addDisposableTo(disposeBag)
// customization using delegate
// RxTableViewDelegateBridge will forward correct messages
tableView.rx_setDelegate(self)
.addDisposableTo(disposeBag)
}
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
tableView.isEditing = editing
}
// MARK: Table view delegate ;)
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let title = dataSource.sectionAtIndex(section)
let label = UILabel(frame: CGRect.zero)
// hacky I know :)
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
}
// MARK: Navigation
private func showDetailsForUser(_ user: User) {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle(identifier: "RxExample-iOS"))
let viewController = storyboard.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
viewController.user = user
self.navigationController?.pushViewController(viewController, animated: true)
}
// MARK: Work over Variable
static func configureDataSource() -> RxTableViewSectionedReloadDataSource<SectionModel<String, User>> {
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, User>>()
dataSource.configureCell = { (_, tv, ip, user: User) in
let cell = tv.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = user.firstName + " " + user.lastName
return cell
}
dataSource.titleForHeaderInSection = { dataSource, sectionIndex in
return dataSource.sectionAtIndex(sectionIndex).model
}
dataSource.canEditRowAtIndexPath = { (ds, ip) in
return true
}
return dataSource
}
}