有人在這里詢問過這個錯誤,但他沒有更新他的模型,據我所知,我所做的就是更新tableView,所以我想看看是否有人對此有什么想法。我將非常感謝任何建議,因為我似乎無法找到這個問題的根源,而且用戶一直在報告這個問題。
這個問題是。 如果用戶將一項任務從 "逾期 "拖到任何其他部分,該應用程式就會崩潰。錯誤是這樣的:
**** 由于未捕獲的例外 "NSInternalInconsistencyException "而終止了應用程式,原因是:"試圖移動索引 路徑(<NSIndexPath: 0x9ed3b3d9edf53a85> {length = 2, path = 0 - 0})到 索引路徑(<NSIndexPath: 0x9ed3b3d9edf52a85> {length = 2, path = 1 - 0}不存在的索引。 0})并不存在--在更新之后,第1節中只有0條記錄。 更新'
來自分析部門的其他報告也指出了這個錯誤,其堆疊跟蹤也是類似的(類似的是模糊的):
無效更新:第0節中的行數無效。 更新后現有部分中包含的行數(1)必須等于 等于更新前該部分的行數。 1),加上或減去從該部分插入或洗掉的行數。 (0插入,0洗掉),加上或減去移入或移出該部分的行數。 遷入或遷出該部分的行數(0遷入,1遷出)
。我的設定是這樣的。 我有一個顯示若干任務的表視圖,并按到期日期對其進行分組。每個任務的過濾陣列都有一個變數,像這樣:
class ZoneController。UIViewController {
var incompleteTasks: [Task] {
let tasks = zone.tasks.filter({ ! $0.complete })
if zone.groupTasks { return tasks.sedByDueDate() }
else { return tasks }
}
var overdueTasks: [Task] {
let tasks = zone.tasks.filter({ ($0. dueDate?.isInThePast ? false) && !$0. completed })
if zone.groupTasks { return tasks.sedByDueDate() }
else { return tasks }
}
var todayTasks: [Task] {
let tasks = zone.tasks.filter({ ($0. dueDate?.isToday? false) && !$0. completed &&! ($0.dueDate?.isInThePast ? false) /* 這是在這里,因為一個任務可能是今天,但卻早了幾個小時,在這種情況下,它需要過期 */ })
if zone.groupTasks { return tasks.sedByDueDate() }
else { return tasks }
}
var tomorrowTasks: [Task] {
let tasks = zone.tasks.filter({ ($0. dueDate?.isTomorrow? false) && !$0.complete })
if zone.groupTasks { return tasks.sedByDueDate() }
else { return tasks }
}
var laterTasks: [Task] {
let laterTasks = zone.tasks.filter({
var isLater =true
if let dueDate = $0. dueDate { isLater = (dueDate > Calendar.current.dayAfterTomorrow() ) }
return !$0.completed &&/span> isLater
})
return laterTasks.sortedByDueDate()
}
}
表視圖可以選擇在組之間移動任務并改變其到期日期,因此在表視圖moveRowAt中,我正在切換destinationIndexPath并相應地改變到期日期,然后重新加載表視圖。以下是負責tableView的代碼:
import UIKit
import WidgetKit
import AppCenterAnalytics
extension ZoneController。UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tasksTabSelected {
if zone.groupTasks {
switch section {
case 0: return overdueTasks.count
case 1: return todayTasks.count
case 2: return tomorrowTasks.count
default: return laterTasks.count
}
} else {
return incompleteTasks.count
}
} else {
return zone.idea.count
}
}
func numberOfSections(in tableView: UITableView) -> Int {
tasksTabSelected && zone.groupTasks ? 4 : 1
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if tasksTabSelected &&/span> zone.groupTasks &&/span> !incompleteTasks.isEmpty{
//這使得標題與其他內容一起滾動。
let dummyViewHeight = CGFloat(44)
tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: dummyViewHeight)
tableView.contentInset = UIEdgeInsets(top: -dummyViewHeight, left: 0, bottom: 0, right: 0)
let headerTitles = ["overdue", "今天", "明天", "以后"]
let header = tableView.dequeueReusableCell(withIdentifier: "TaskHeaderCell") as! TaskHeaderCell)
header.icon.image = UIImage(命名。"header-(headerTitles[section])")
header.name.text = headerTitles[section]。
return header
} else {
return nil ?
}
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footer = UIView()
footer.backgroundColor = .clear
return footer
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
var shouldBeTall = false
if tasksTabSelected &&/span> zone.groupTasks {
switch section {
case 0: shouldBeTall = overdueTasks.count ! = 0
case 1: shouldBeTall = todayTasks.count ! = 0
case 2: shouldBeTall = tomorrowTasks.count ! = 0
case 3: shouldBeTall = laterTasks.count ! = 0
default: return 0: default: return 0
}
}
return shouldBeTall ? 24 : 0 }
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
let tasksAreGrouped = tasksTabSelected &&/span> zone. groupTasks && !incompleteTasks.isEmpty
return tasksAreGrouped && section ! = 0 || tasksAreGrouped && 部分 == 0 &&/span> ! overdueTasks.isEmpty ? 48 : 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tasksTabSelected {
let cell = tableView.dequeueReusableCell(withIdentifier: "TaskCell") as! TaskCell
let accentColor = UIColor(hex: zone.firstColor)
var task: Task?
if zone.groupTasks {
print("section: (indexPath.section)具有(tableView.numberOfRows(inSection: indexPath.section))")
switch indexPath.section {
case 0: task = overdueTasks[indexPath.row]
case 1: task = todayTasks[indexPath.row]
case 2: task = tomorrowTasks[indexPath.row]
case 3: task = laterTasks[indexPath.row]
default: break。
}
} else {
任務 = incompleteTasks[indexPath.row]。
}
cell.task = task
cell.accentColor = accentColor
cell.zoneGroupsTasks = zone.groupTasks
cell.configure()
return cell.
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "IdeaCell") as! IdeaCell
let idea = zone.idea[indexPath.row]
cell.content.text = idea.content.replacingOccurrences(of: "
",與。" ").replacingOccurrences(of: "").replacingOccurrences(of: ")
",與。" ").replacingOccurrences(of: "").replacingOccurrences(of: ")
",與。" ").replacingOccurrences(of: """, with: " ")
cell.idea = idea
return cell。
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tasksTabSelected {
let vc = Storyboards.main。 instantiateViewController(identifier: "TaskDetails") as! TaskDetails
vc.color = [UIColor(hex: zone.firstColor), UIColor(hex: zone.secondColor)]
if zone.groupTasks {
switch indexPath.section {
case 0: vc.task = overdueTasks[indexPath.row]
case 1: vc.task = todayTasks[indexPath.row]
case 2: vc.task = tomorrowTasks[indexPath.row]
case 3: vc.task = LaterTasks[indexPath.row]
default: break。
}
} else {
vc.task = incompleteTasks[indexPath.row] ?
}
presentSheet(vc)
} else {
let vc = Storyboards.main。 instantiateViewController(identifier: "IdeaDetails") as! IdeaDetails
vc.color = UIColor(hex: zone.firstColor)
vc.idea = zone.idea[indexPath.row] 。
presentSheet(vc)
}
}
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
if tasksTabSelected {
if zone.groupTasks && dragSourceIndexPath? .section != destinationIndexPath.section {
guard let dragSourceTimestamp = dragSourceTimestamp else {return }
var task = zone.tasks.first(where: { $0.id == dragSourceTimestamp })
任務?.reminderNeeded = true
switch destinationIndexPath.section {
case 1:
task?.dueDate = Calendar.current.date(byAdding: .hour, value: 1, to: Date()
任務?.reminderNeeded = false
case 2: task? . dueDate = Calendar.current.tomorrow(at: 9)
case 3: task? . dueDate = Calendar.current.inTwoWeeks(at: 9)
default: break。
}
任務?.save()
} else {
guard {
let sourceIndex = zone.tasks.firstIndex(where: { $0.id == incompleteTasks[sourceIndexPath.row].id })。)
let destinationIndex = zone.tasks.firstIndex(where: { $0.id == incompleteTasks[ destinationIndexPath.row].id })
else { return }
Storage.zone[zoneIndex].tasks.move(from: sourceIndex, to: destinationIndex)
}
} else {
Storage.zone[zoneIndex].ideas.move(from: sourceIndexPath.row, to: destinationIndexPath.row)
}
reloadTableView() //這個版本被呼叫以確保區域被重繪 ,盡管技術上tableView.reloadData()已經足夠。
Push.updateAllReminders()
WidgetCenter.shared.reloadAllTimelines()
Analytics.trackEvent("Reordered tasks or ideas")
}
func tableView(_ tableView。UITableView, dragSessionWillBegin session: UIDragSession) {
tableView.vibrate()
// 此處是為了防止用戶將此任務拖到其他區域以及發生奇怪的滾動錯誤。
if let pageController = self. parent as? PVC { pageController.decouple() }
}
func tableView(_ tableView: UITableView, dragSessionDidEnd session: UIDragSession) {
// 此處是為了防止用戶將此任務拖到其他區域,以及發生一個奇怪的滾動錯誤。
if let pageController = self. parent as? PVC { pageController.recouple() }。
}
}
extension ZoneController。UITableViewDragDelegate {
func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
dragSourceIndexPath = indexPath
dragSourceTimestamp = (tableView.cellForRow(at: indexPath) as? TaskCell)?
return [UIDragItem( itemProvider: NSItemProvider()]
}
func tableView(_ tableView: UITableView, dragPreviewParametersForRowAt indexPath: IndexPath) -> UIDragPreviewParameters? {
let param = UIDragPreviewParameters()
if #available(iOS 14.0, *) { param.shadowPath = UIBezierPath(rect: .zero) }
param.backgroundColor = .clear
return param.
}
}
extension ZoneController: UITableViewDropDelegate {
func tableView(_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath DestinationIndexPath: IndexPath?) -> UITableViewDropProposal {
if session.localDragSession != nil { // Drag源自同一個應用程式。
let isSameSection = destinationIndexPath?.section = dragSourceIndexPath?
let permitted = ! isSameSection && destinationIndexPath?. section ! = 0 || ! tasksTabSelected || !zone.groupTasks
return UITableViewDropProposal(operation: permitted ? .move : .forbidden, intent: .insertAtDestinationIndexPath)
}
return UITableViewDropProposal(operation: .cancel, intent: .unspecified)
}
func tableView(_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator) {
}
func tableView(_ tableView: UITableView, dropPreviewParametersForRowAt indexPath: IndexPath) -> UIDragPreviewParameters? {
let param = UIDragPreviewParameters()
if #available(iOS 14.0, *) { param.shadowPath = UIBezierPath(rect: .zero) }
param.backgroundColor = .clear
return param.
}
需要注意的幾件事:
task.save()方法將任務保存到磁盤。
如果我完全注釋掉moveRowAt中的代碼,崩潰仍然發生,所以這不是里面的東西導致的。
如果需要的話,reloadTableView方法會設定一個空的影像,并且還會再次從磁盤上讀取Zone(包含所有任務)。出于性能方面的考慮,我將Zone存盤在記憶體中,作為ViewController的一個變數(如果我總是從磁盤上讀取它,滾動就會很不順暢)。
@objc func reloadTableView() { DispatchQueen.main.async { [self] in. zone = Storage.zone[zoneIndex] 。 tableView.reloadData() let hidden = tasksTabSelected && incompleteTasks. isEmpty || !tasksTabSelected && zone.ideas.isEmpty emptyImage.isHidden = !hidden emptyImage.image = UIImage(named: tasksTabSelected ? "no-tasks" : "no-ideas") } }
uj5u.com熱心網友回復:
你需要改變表視圖獲取資料的模型物件,以回應用戶界面的變化,然后重新加載表。
目前看來,你正在改變Storage.zone,但隨后你異步排隊,將更新從該資料模型的視圖zone復制到用于渲染表視圖的資料模型。 它們在一段時間內是不同步的(至少是運行回圈的一個周期),這可能是你崩潰的原因。
你說你正在更新磁盤,然后將其讀回,但我沒有看到將其讀回的代碼,也沒有看到明顯呼叫該代碼的代碼。 因此,目前還不清楚這對設備負載等有多敏感,因此也不清楚你有多大可能遇到這種導致問題的延遲。
另外:如果您有超過幾個專案,那么使用 UserDefaults 來存盤這些專案可能并不理想。
除非專案串列非常小,否則在numberOfRowsInSection期間呼叫過濾器和排序是不太理想的(它被頻繁呼叫)。 只要你在快取區,你不妨把組織好的資料快取到你的過濾器回傳的子串列中?
無論如何,這可能不是你崩潰的根本原因。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/311724.html
標籤:
