私は新しい Swifter です。これが私の新しい会社のコードです。
RxSwift を使用する、RxDataSource を使用する、2 つのテーブル ビューの関連付けを処理する方法は?
左の tableView のセルがクリックされ、右の tableView のデータが変更されました。
中間状態の変数を使用して、右側のテーブル ビューのデータを整理します。
悪いコードの匂い。</p>
画像はこちら
コードはこちら:</p>
private let viewModel = CategoryViewModel()
private var currentListData :[SubItems]?
private var lastIndex : NSInteger = 0
private var currentSelectIndexPath : IndexPath?
private var currentIndex : NSInteger = 0
private func boundTableViewData() {
var loadCount = 0
// data source of left table view
let dataSource = RxTableViewSectionedReloadDataSource<CategoryLeftSection>( configureCell: { ds, tv, ip, item in
let cell = tv.dequeueReusableCell(withIdentifier: "Cell1", for: ip) as! CategoryLeftCell
cell.model = item
if ip.row == 0, !cell.isSelected {
// in order to give the right table view a start show
tv.selectRow(at: ip, animated: false, scrollPosition: .top)
tv.delegate?.tableView!(tv, didSelectRowAt: ip)
}
return cell
})
vmOutput!.sections.asDriver().drive(leftMenuTableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
// organize the right table view's data via the variables of middle state.
// bad code's smell
let listData = leftMenuTableView.rx.itemSelected.distinctUntilChanged().flatMapLatest {
[weak self](indexPath) -> Observable<[SubItems]> in
guard let self = self else { return Observable.just([]) }
// ...
self.currentIndex = indexPath.row
if indexPath.row == self.viewModel.vmDatas.value.count - 1 {
// ...
// the self.currentSelectIndexPath was used, because when the left tableView's final cell got clicked, the UI logic is different.
self.leftMenuTableView.selectRow(at: self.currentSelectIndexPath, animated: false, scrollPosition: .top)
return Observable.just((self.currentListData)!)
}
if let subItems = self.viewModel.vmDatas.value[indexPath.row].subnav {
var fisrtSubItem = SubItems()
fisrtSubItem.url = self.viewModel.vmDatas.value[indexPath.row].url
fisrtSubItem.name = self.viewModel.vmDatas.value[indexPath.row].banner
var reult:[SubItems] = subItems
reult.insert(fisrtSubItem, at: 0)
self.currentListData = reult
// self.currentListData is used to capture the current data of the right table view.
self.currentSelectIndexPath = indexPath
return Observable.just(reult)
}
return Observable.just([])
}.share(replay: 1)
// data source of right table view
let listDataSource = RxTableViewSectionedReloadDataSource<CategoryRightSection>( configureCell: { [weak self]ds, tv, ip, item in
guard let self = self else { return UITableViewCell() }
if self.lastIndex != self.currentIndex {
// to compare the old and new selected index of the left table View ,give a new start to the right table view if changed
tv.scrollToRow(at: ip, at: .top, animated: false)
self.lastIndex = self.currentIndex
}
if ip.row == 0 {
let cell = CategoryListBannerCell()
cell.model = item
return cell
} else {
let cell = tv.dequeueReusableCell(withIdentifier: "Cell2", for: ip) as! CategoryListSectionCell
cell.model = item
return cell
}
})
listData.map{ [CategoryRightSection(items:$0)] }.bind(to: rightListTableView.rx.items(dataSource: listDataSource))
.disposed(by: rx.disposeBag)
}
private var lastIndex : NSInteger = 0
currentIndex
、左側のテーブル ビューの古い選択インデックスと新しい選択インデックスを比較するために使用されます。異なる場合は、右側のテーブル ビューを開始します。
これself.currentSelectIndexPath
が使用されたのは、左の tableView の最後のセルがクリックされたときの UI ロジックが異なるためです。
self.currentListData
左側の tableView が別の行でクリックされたときに、右側のテーブル ビューの現在のデータをキャプチャするために使用されます。
self.currentListData
にも使用されますUITableViewDelegate
。
// MARK:- UITableViewDelegate
extension CategoryViewController : UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch indexPath.row {
case 0 :
return (mScreenW - 120)/240 * 100;
default :
let subItems:SubItems = self.currentListData![indexPath.row]
if subItems.children.count > 0{
let lines: NSInteger = (subItems.children.count - 1)/3 + 1
let buttonHeight = (mScreenW - 136 - 108)/3
let allButtonHeight = buttonHeight/44 * 63 * CGFloat(lines)
let other = (lines - 1)*42 + 56
let height = allButtonHeight + CGFloat(other) + 33
return height
}
return 250
}
}
}
コードを改善するには?
中間状態の変数を削除する方法。
対応機種は
class CategoryViewModel: NSObject {
let vmDatas = Variable<[ParentItem]>([])
func transform() -> MCBoutiqueOutput {
let temp_sections = vmDatas.asObservable().map({ (sections) -> [CategoryLeftSection] in
let count = sections.count
if count > 0{
let items = sections[0..<(count-1)]
return [CategoryLeftSection(items: Array(items))]
}
return []
}).asDriver(onErrorJustReturn: [])
let output = MCBoutiqueOutput(sections: temp_sections)
Observable.combineLatest(output.requestCommand, Provider.rx.cacheRequest(.baseUIData)).subscribe({ [weak self] ( result: Event<(Bool, Response)>) in
guard let self = self else { return }
switch result{
case .next(let response):
let resultReal = response.1
// ...
if resultReal.statusCode == 200 || resultReal.statusCode == 230 {
if resultReal.fetchJSONString(keys:["code"]) == "0" {
mUserDefaults.set(false, forKey: "categoryVCShowTry")
self.vmDatas.value = ParentItem.mapModels(from:
resultReal.fetchJSONString(keys:["data","data"]))
}
}
default:
break
}
}).disposed(by: rx.disposeBag)
return output
}
}