Skip to content

Instantly share code, notes, and snippets.

@abdulowork
Created December 28, 2016 00:29
Show Gist options
  • Save abdulowork/632d136db735846372e8ae28ae244c06 to your computer and use it in GitHub Desktop.
Save abdulowork/632d136db735846372e8ae28ae244c06 to your computer and use it in GitHub Desktop.
import UIKit
protocol ItemViewBinder {
func bind(model: Item,to view: ItemViewType)
}
extension ItemViewBinder {
func bind(model: Item,to view: ItemViewType) {
view.bonusLabel.text = "+ \(Int(model.bonus)) балл" + String.endingForBonusLabel(for: Int(model.bonus))
view.itemImageView.image = model.image
view.titleLabel.text = "\(model.title!)"
view.weightLabel.text = "\(Int(model.weight)) гр."
view.priceLabel.attributedText = NSMutableAttributedString(value: model.price, additionalTitle: " ₽/шт", font: UIFont.boldSystemFont(ofSize: 22))
if view is OldPriceViewType {
if let originalPrice = model.originalPrice {
let oldPriceAttributedString = NSMutableAttributedString(value: originalPrice, additionalTitle: "", font: UIFont.boldSystemFont(ofSize: 12))
oldPriceAttributedString.addAttribute(NSStrikethroughStyleAttributeName, value: NSNumber(value: 2), range: NSRange(location: 0, length: oldPriceAttributedString.length))
(view as! OldPriceViewType).oldPriceLabel.attributedText = oldPriceAttributedString
}
}
}
}
import UIKit
import RxSwift
import RxDataSources
import MGSwipeTableCell
class OrderViewController: UIViewController, ItemViewBinder, ItemCollectionViewBinder, ShieldingsBinder, SaleBinder {
let disposeBag = DisposeBag()
@IBOutlet weak var weightNavBarLabel: UILabel!
@IBOutlet weak var itemCounterNavBarLabel: UILabel!
@IBOutlet weak var totalPriceNavBarLabel: UILabel!
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var orderProcessingButton: UIButton!
let order = Order.current
var orderItems = Order.current.itemCollections
var itemCollectionsToDelete: [ItemCollection] = []
var salesSet: Set<Sale> = Set()
override func viewDidLoad() {
super.viewDidLoad()
headerLabelsSetup()
tableView.register(UINib(nibName: "ItemCollectionTableViewCell", bundle: nil), forCellReuseIdentifier: "ItemCollectionTableViewCell")
tableView.register(UINib(nibName: "OptionCell", bundle: nil), forCellReuseIdentifier: "OptionCell")
tableView.register(UINib(nibName: "OrderSaleCell", bundle: nil), forCellReuseIdentifier: "OrderSaleCell")
tableView.register(UINib(nibName: "ReplacementCell", bundle: nil), forCellReuseIdentifier: "ReplacementCell")
tableView.estimatedRowHeight = 155
tableView.rowHeight = UITableViewAutomaticDimension
let staticOptionsSection: OrderMultipleSectionModel =
.optionsSection(
title: "ОПЦИИ",
items: (1...3).map{ _ in .optionsSectionItem(image: UIImage(named: "bonus-card")!, title: String.randomTitle()) }
)
let staticSalesSection: OrderMultipleSectionModel =
.salesSection(
title: "АКЦИИ",
items: (1...3).map{ _ in .salesSectionItem(sale: MockSaleFactory().superPriceSale()) }
)
let staticReplacementSection: OrderMultipleSectionModel =
.replacementItemsSection(title: "НЕТ В НАЛИЧИИ", items: [
.replacementSectionItem(
itemToReplace: MockItemFactory().danone(),
replacementItem: MockItemFactory().danone()
)]
)
let dataSource = RxTableViewSectionedAnimatedDataSource<OrderMultipleSectionModel>()
skinTableViewDataSource(dataSource)
Observable.combineLatest(
orderItems.map{ .itemsSection(title: "Section", items: $0.map{ .itemsSectionItem(model: $0) }) },
Observable.just(staticReplacementSection),
Observable.just(staticOptionsSection),
Observable.just(staticSalesSection)
) {
return [$0.0, $0.1, $0.2, $0.3]
}
.shareReplay(1)
.bindTo(tableView.rx.items(dataSource: dataSource))
.addDisposableTo(disposeBag)
orderProcessingButton.rx.tap
.subscribe(onNext: { [unowned self] _ in self.performSegue(withIdentifier: "OrderProcessing", sender: nil) })
.addDisposableTo(disposeBag)
}
func headerLabelsSetup() {
order.totalWeight.map { weight in
let text = NSMutableAttributedString(string: "Вес: ")
text.append(NSAttributedString(string: String(format: "%.1f", weight/1000) + " кг.", attributes:
[
NSFontAttributeName : UIFont.boldSystemFont(ofSize: self.weightNavBarLabel.font.pointSize),
]
))
return text
}.bindTo(weightNavBarLabel.rx.attributedText).addDisposableTo(disposeBag)
order.totalNumberOfItems.map { itemsCount in
let text = NSMutableAttributedString(string: "Товаров: ")
text.append(NSAttributedString(string: "\(itemsCount)", attributes:
[
NSFontAttributeName : UIFont.boldSystemFont(ofSize: self.itemCounterNavBarLabel.font.pointSize),
]
))
return text
}.bindTo(itemCounterNavBarLabel.rx.attributedText).addDisposableTo(disposeBag)
order.totalPrice.map { price in
let text = NSMutableAttributedString(string: "Итого: ")
let totalPrice = NSMutableAttributedString(
value: price,
additionalTitle: " ₽",
font: UIFont.boldSystemFont(ofSize: self.totalPriceNavBarLabel.font.pointSize+2))
totalPrice.addAttribute(NSForegroundColorAttributeName, value: PerekrestokColor.lightGreen, range: NSRange(location: 0, length: totalPrice.length))
text.append(totalPrice)
return text
}.bindTo(totalPriceNavBarLabel.rx.attributedText).addDisposableTo(disposeBag)
}
fileprivate func skinTableViewDataSource(_ dataSource: RxTableViewSectionedAnimatedDataSource<OrderMultipleSectionModel>) {
dataSource.configureCell = { dataSource, tableView, indexPath, _ in
switch dataSource[indexPath] {
case .itemsSectionItem(model: let model):
let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCollectionTableViewCell", for: indexPath) as! ItemCollectionTableViewCell
self.bind(model: model, to: cell)
self.bind(shieldings: ShieldingLabel.randomShieldingSet(), to: cell)
cell.pickerButton.rx.tap
.subscribe(onNext: { [unowned self] in self.performSegue(withIdentifier: "ItemAmountPicker", sender: model) })
.addDisposableTo(cell.rx_reusableDisposeBag)
if (self.itemCollectionsToDelete.contains(model)) {
let cover = DeletedCellCoverButton(type: .custom)
cover.backgroundColor = UIColor.white
cover.alpha = 0.9
cell.contentView.addSubview(cover)
cell.coverButton = cover
cell.coverButton?.rx.tap
.subscribe(onNext: { [unowned self] in
try! self.itemCollectionsToDelete.remove(item: model)
cell.coverButton?.removeFromSuperview()
cell.coverButton = nil
tableView.reloadRows(at: [indexPath], with: .none)
}).addDisposableTo(cell.rx_reusableDisposeBag)
cover.snp.makeConstraints { make in
make.top.equalToSuperview()
make.bottom.equalToSuperview()
make.leading.equalToSuperview()
make.trailing.equalToSuperview()
}
} else {
cell.rightSwipeSettings.transition = .border
let deleteButton = MGSwipeButton(
title: "Удалить",
icon: UIImage(named: "trash"),
backgroundColor: PerekrestokColor.lightRed) { _ in
self.itemCollectionsToDelete.append(model)
self.tableView.reloadRows(at: [indexPath], with: .fade)
return true
}
deleteButton.centerIconOverText(withSpacing: 8)
deleteButton.setTitleColor(UIColor.red, for: .normal)
cell.rightButtons = [deleteButton]
}
return cell
case .replacementSectionItem(itemToReplace: let itemToReplace, replacementItem: let replacementItem):
let cell = tableView.dequeueReusableCell(withIdentifier: "ReplacementCell", for: indexPath) as! ReplacementCell
self.bind(model: itemToReplace, to: cell.cellToReplace)
self.bind(model: replacementItem, to: cell.replacementCell)
FavoriteItems.current
.contains(replacementItem)
.bindTo(cell.replacementCell.favoriteButton.rx.isSelected)
.addDisposableTo(cell.replacementCell.rx_reusableDisposeBag)
cell.replacementCell.addToOrderButton.rx.tap.subscribe(onNext: { _ in
Order.current.appendItem(replacementItem)
})
.addDisposableTo(cell.replacementCell.rx_reusableDisposeBag)
cell.replacementCell.favoriteButton.rx.tap.subscribe(onNext: { _ in
FavoriteItems.current.pushItem(replacementItem)
})
.addDisposableTo(cell.replacementCell.rx_reusableDisposeBag)
return cell
case .optionsSectionItem(image: let image, title: let title):
let cell = tableView.dequeueReusableCell(withIdentifier: "OptionCell", for: indexPath) as! OptionCell
cell.optionImageView.image = image
cell.titleLabel.text = title
return cell
case .salesSectionItem(sale: let sale):
let cell = tableView.dequeueReusableCell(withIdentifier: "OrderSaleCell", for: indexPath) as! OrderSaleCell
self.bind(sale: sale, to: cell)
cell.selectionButton.isSelected = self.salesSet.contains(sale)
return cell
}
}
//MARK: - Animations
dataSource.animationConfiguration = AnimationConfiguration(
insertAnimation: .left,
reloadAnimation: .none,
deleteAnimation: .left
)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
let currentDataState = try! orderItems.value()
orderItems.on(.next(Set(currentDataState.filter { !itemCollectionsToDelete.contains($0) })))
itemCollectionsToDelete.removeAll()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.destination {
case is ItemAmountPickerViewController:
let destionation = segue.destination as! ItemAmountPickerViewController
destionation.model = sender as! ItemCollection
destionation.insertionSet = Order.current.itemCollections
default:
break
}
}
}
extension OrderViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
switch section {
case 0:
return nil
case 1:
return HeaderView(title: "НЕТ В НАЛИЧИИ")
case 2:
return HeaderView(title: "ОПЦИИ")
case 3:
return HeaderView(title: "АКЦИИ")
default:
fatalError()
}
}
}
fileprivate enum OrderMultipleSectionModel {
case itemsSection(title: String, items: [OrderSectionItem])
case replacementItemsSection(title: String, items: [OrderSectionItem])
case optionsSection(title: String, items: [OrderSectionItem])
case salesSection(title: String, items: [OrderSectionItem])
}
fileprivate enum OrderSectionItem {
case itemsSectionItem(model: ItemCollection)
case replacementSectionItem(itemToReplace: Item, replacementItem: Item)
case optionsSectionItem(image: UIImage, title: String)
case salesSectionItem(sale: Sale)
}
extension OrderSectionItem: Equatable {
static func ==(lhs: OrderSectionItem, rhs: OrderSectionItem) -> Bool {
switch (lhs, rhs) {
case (.itemsSectionItem(model: let lhsModel),
.itemsSectionItem(model: let rhsModel)):
return lhsModel == rhsModel && lhsModel.amount == rhsModel.amount
case (.replacementSectionItem(itemToReplace: let lhsItem, replacementItem: _),
.replacementSectionItem(itemToReplace: let rhsItem, replacementItem: _)):
return lhsItem == rhsItem
case (.optionsSectionItem(image: _, title: let lhsTitle),
.optionsSectionItem(image: _, title: let rhsTitle)):
return lhsTitle == rhsTitle
case (.salesSectionItem(sale: let lhsSale),
.salesSectionItem(sale: let rhsSale)):
return lhsSale == rhsSale
default:
return false
}
}
}
extension OrderSectionItem: IdentifiableType {
typealias Identity = Int
var identity: Int {
switch self {
case .itemsSectionItem(model: let model):
return model.identity
case .optionsSectionItem(image: _, title: let title):
return title.hashValue
case .replacementSectionItem(itemToReplace: let itemToReplace, replacementItem: _):
return itemToReplace.hashValue
case .salesSectionItem(sale: let sale):
return sale.identity
}
}
}
extension OrderMultipleSectionModel: AnimatableSectionModelType {
//MARK: - SectionModelType
typealias Item = OrderSectionItem
var items: [Item] {
switch self {
case .itemsSection(title: _, items: let items):
return items
case .replacementItemsSection(title: _, items: let items):
return items
case .optionsSection(title: _, items: let items):
return items
case .salesSection(title: _, items: let items):
return items
}
}
var title: String {
switch self {
case .itemsSection(title: let title, items: _):
return title
case .replacementItemsSection(title: let title, items: _):
return title
case .optionsSection(title: let title, items: _):
return title
case .salesSection(title: let title, items: _):
return title
}
}
init(original: OrderMultipleSectionModel, items: [Item]) {
switch original {
case .itemsSection(title: let title, items: _):
self = .itemsSection(title: title, items: items)
case .replacementItemsSection(title: let title, items: _):
self = .replacementItemsSection(title: title, items: items)
case .optionsSection(title: let title, items: _):
self = .optionsSection(title: title, items: items)
case .salesSection(title: let title, items: _):
self = .salesSection(title: title, items: items)
}
}
//MARK: - IdentifiableType
typealias Identity = String
var identity: String {
return title
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment