Created
December 28, 2016 00:29
-
-
Save abdulowork/632d136db735846372e8ae28ae244c06 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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