在我的移动应用程序中,我想通过一次拉动来更新tableView数据源以刷新请求,但是我不知道如何在tableview数据源的顶部插入新项。
我看到有一个insertRows的方法,例如:self.tableView?.insertRows(at:[indexPath],带有:.top),但是如何根据我的方法在此处添加newItems?
我有一个名为initializedTableView()的函数,该函数使用PublishSubject可观察的项初始化tableView。
func initializeTableView() {
viewModel
.items
.subscribe(onNext: { items in
self.tableView?.delegate = nil
self.tableView?.dataSource = nil
Observable.just(items)
.bind(to:(self.tableView?.rx.items(cellIdentifier:
itemCell.Identifier, cellType: itemCell.self))!) {
(index, element, cell) in
cell.itemModel = element
}.disposed(by: self.disposeBag)
})
.disposed(by: disposeBag)
}
一旦用户请求拉动刷新,就会调用此函数:
func refreshTableView() {
// get new items
viewModel
.newItems
.subscribe(onNext: { newItems in
//new
let new = newItems.filter({ item in
// items.new == true
})
//old
var old = newItems.filter({ item -> Bool in
// items.new == false
})
new.forEach({item in
// how to update tableView.rx.datasource here???
})
}).disposed(by: disposeBag)
}
答案 0 :(得分:0)
由于tableView.insertRows
出现问题,我对我的应用做了类似的操作。
代码如下:
func loadMoreComments() {
// call to backend to get more comments
getMoreComments { (newComments) in
// combine the new data and your existing data source array
self.comments = newComments + self.comments
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
// calculate the total height of the newly added cells
var addedHeight: CGFloat = 0
for i in 0...result.count {
let indexRow = i
let tempIndexPath = IndexPath(row: Int(indexRow), section: 0)
addedHeight = addedHeight + self.tableView.rectForRow(at: tempIndexPath).height
}
// adjust the content offset by how much height was added to the start so that it looks the same to the user
self.tableView.contentOffset.y = self.tableView.contentOffset.y + addedHeight
}
}
因此,通过计算要添加到开始处的新单元格的高度,然后将此计算出的高度添加到tableView.contentOffset.y
中,我能够无缝地将单元格添加到tableView的顶部,而无需重做我的{{ 1}}。这看起来像是一个比较麻烦的解决方法,但是如果您正确计算高度,tableView
中的偏移不会很明显。
答案 1 :(得分:0)
struct ViewModel {
let items: Observable<[Item]>
init(trigger: Observable<Void>, newItems: @escaping () -> Observable<[Item]>) {
items = trigger
.flatMapLatest(newItems)
.scan([], accumulator: { $1 + $0 })
}
}
以上内容不处理错误,也不处理重置,但是scan
会将新项目放在列表的顶部。
尽管这种情况并不正确。通常,API调用返回所有项目,怎么可能知道哪些项目是“新的”?
答案 2 :(得分:0)
struct ViewModel {
let items: BehaviorRelay<[Item]>
init() {
self.items = BehaviorRelay(value: [])
}
func fetchNewItems() {
// This assumes you are properly distinguishing which items are new
// and `newItems` does not contain existing items
let newItems: [Item] = /* However you get new items */
// Get a copy of the current items
var updatedItems = self.items.value
// Insert new items at the beginning of currentItems
updatedItems.insert(contentsOf: newItems, at: 0)
// For simplicity this answer assumes you are using a single cell and are okay with a reload
// rather than the insert animations.
// This will reload your tableView since 'items' is bound to the tableView items
//
// Alternatively, you could use RxDataSources and use the `RxTableViewSectionedAnimatedDataSource`
// This will require a section model that conforms to `AnimatableSectionModelType` and some
// overall reworking of this example
items.accept(updatedItems)
}
}
final class CustomViewController: UIViewController {
deinit {
disposeBag = DisposeBag()
}
@IBOutlet weak var tableView: UITableView!
private var disposeBag = DisposeBag()
private let viewModel = ViewModel()
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(CustomTableCell.self, forCellReuseIdentifier: "ReuseID")
tableView.refreshControl = UIRefreshControl()
viewModel.items
.bind(to: tableView.rx.items(cellIdentifier: "ReuseID", cellType: CustomTableCell.self)) { row, item, cell in
// Configure cell with item
cell.configure(with: item)
}
.disposed(by: disposeBag)
tableView.refreshControl?.rx.controlEvent(.valueChanged)
.subscribe(onNext: { [weak self] in
self?.viewModel.fetchNewItems()
})
.disposed(by: disposeBag)
}
}
使用BehaviorRelay
和绑定的替代答案。这样,您只需更新items
中继,它将自动更新tableView。它还提供了一种更“ Rx”的方式来处理拉动刷新。
如代码注释中所述,这假定您正在确定哪些项目是新项目,并且newItems
不包含任何现有项目。无论哪种方式都应该提供一个起点。