Ok, the setup for this is pretty complex. I'm doing maintenance on an app I did not write. The view structure is as follows:
At the base you have a UICollectionViewController. Each UICollectionViewCell has an assigned UIViewController within it. Each of these controllers is attached to the view hierarchy as a child view controller. The main view of one of these child controllers is a UITableView/UIViewController pair.
Everything seems to be hooked up properly and working well except for one case. Assuming the table-view has more than two rows, If I swipe-left to delete the second row (IndexPath 0,1), the content or row one (IndexPath 0,0) goes blank. I can see the disclosure accessory view but the two UILabels up and disappear. If I tap on the blank row the tap behavior is still intact. If I refresh the table-view, the missing content appears as it should.
To make things a little more interesting, this table-view is implement using RxSwift. Here is the code:
private func bind() {
// Cell for row at index path.
let curriedArgument: (Int, Assignment, AssessmentTableViewCell) - Void = { /* rowIndex */_, model, cell in
cell.assignment = model
}
let observer: Observable[Assignment]
// For the widget version of this controller, only display the first N
// items in a table-view that does not scroll. If more than N items exist
// the user must tap the `More` view. For the non-widget view, show all
// available items in a scrolling table-view.
if isWidget {
tableView.isScrollEnabled = false
observer = viewModel
.assignments
.asObservable()
.map({ Array($0.prefix(CPAssignmentWidgetVC.numberOfItemsToDisplay))})
} else {
observer = viewModel
.assignments
.asObservable()
}
observer
.distinctUntilChanged()
.bind(to: tableView.rx.items(cellIdentifier: "AssessmentCell"), curriedArgument: curriedArgument)
.disposed(by: disposeBag)
// When something changes, update both the widget and the modal view as
// needed.
if isWidget {
Observable
.combineLatest(viewModel.fetching.asObservable(),
viewModel.assignments.asObservable())
.subscribe(onNext: { [weak self] (isFetching, assignments) in
self?.updateFooter(with: assignments, isFetching: isFetching)
})
.disposed(by: disposeBag)
} else {
viewModel.fetching.asObservable()
.distinctUntilChanged()
.debounce(.milliseconds(500), scheduler: MainScheduler.instance)
.subscribe(onNext: { isFetching in
if isFetching {
SVProgressHUD.show()
} else {
SVProgressHUD.dismiss()
}
})
.disposed(by: disposeBag)
}
// Select cell
tableView
.rx
.itemSelected
.subscribe(onNext: { [unowned self] indexPath in
self.tableView.deselectRow(at: indexPath, animated: true)
guard
let cell = tableView.cellForRow(at: indexPath) as? AssessmentTableViewCell,
let assignment = cell.assignment else { return }
if cell.assignmentResult != nil {
presentOptions(cell)
} else {
guard let patient = patient, patient.hasPermissionToModify(permissionType: .assessments) else {
let title = NSLocalizedString("We're sorry!", comment: "Notification error title")
let message = NSLocalizedString("You don't have permission to complete this survey", comment: "Notification error message")
let dismiss = NSLocalizedString("Dismiss", comment: "Button title")
LHDNotification.show(with: title, text: message, type: .error).setDismissButtonTitle(dismiss)
return
}
presentAssignmentEntryForm(assignment)
}
})
.disposed(by: disposeBag)
// Delete cell (remove associated backing model value)
tableView
.rx
.itemDeleted
.subscribe(onNext: { indexPath in
self.viewModel.remove(at: indexPath.row)
})
.disposed(by: disposeBag)
// Display last cell
tableView
.rx
.willDisplayCell
.subscribe(onNext: { [unowned self] (/* cell */_, indexPath) in
if indexPath.item + 1 == self.viewModel.numberOfItems {
self.viewModel.loadMore()
}
})
.disposed(by: disposeBag)
}
}
Question: Why is the first row content being wiped out? Assuming no one can know the answer to this I have a follow-up question. How can I get the contents of the entire tableview to redraw themselves after a successful delete? I would have assumed the binding to the table-view on line 27 would handle this automatically when the view-model updates.