I have a master view application where is store events and some subevents under the events... When i add subevents i can add upto 4 subevents without the application crashing but as soon as i add the 5th event the application crashes giving the following error
Fatal error: Unexpectedly found nil while unwrapping an Optional value: file /Users/mike/EventTracker/Controller/DetailViewController.swift, line 303
2020-06-05 23:15:17.030027+0530 Progress Tracker[66453:2131961] Fatal error: Unexpectedly found nil while unwrapping an Optional value: file /Users/mike/EventTracker/Controller/DetailViewController.swift, line 303
(lldb)
This is the code for the update
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
table.insertRows(at: [newIndexPath!], with: .fade)
case .delete:
table.deleteRows(at: [indexPath!], with: .fade)
case .update:
configureTable(table.cellForRow(at: indexPath!)! as! SubEventDetailCell, withEvent: anObject as! SubEvent, index: indexPath!.row)
case .move:
configureTable(table.cellForRow(at: indexPath!)! as! SubEventDetailCell, withEvent: anObject as! SubEvent, index: indexPath!.row)
default:
return
}
configureView()
}
The configureTable method creates a new instance of the cell and updates the labels
func configureTable(_ cell: UITableViewCell, withEvent task: Task, index: Int) {
let tableCell = cell as! SubEventDetailCell;
tableCell.initCell(name: task.name ?? "", endDate: task.endDate ?? Date(), notes: task.notes ?? "" , taskNumber: index + 1 )
}
I do get this warning message when the details are been viewed for the first time
Warning once only: UITableView was told to layout its visible cells and other contents without being in the view hierarchy (the table view or one of its superviews has not been added to a window). This may cause bugs by forcing views inside the table view to load and perform layout without accurate information (e.g. table view bounds, trait collection, layout margins, safe area insets, etc), and will also cause unnecessary performance overhead due to extra layout passes. Make a symbolic breakpoint at UITableViewAlertForLayoutOutsideViewHierarchy to catch this in the debugger and see what caused this to occur, so you can avoid this action altogether if possible, or defer it until the table view has been added to a window. Table view: <UITableView: 0x7fa373859a00; frame = (0 416; 991 608); clipsToBounds = YES; hidden = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x600003c9b690>; layer = <CALayer: 0x6000032cb520>; contentOffset: {0, 0}; contentSize: {991, 0}; adjustedContentInset: {0, 0, 20, 0}; dataSource: <EventTracker.DetailViewController: 0x7fa3760075d0>>
This code works fine until I add up to 4 subevents but from the 5th afterwards the application keeps crashing.Is there anyway i can fix this?
Update : I manually set the row height for 200 in the table view, I ran a test by changing it to 20 then i was able to keep on adding the subevents, but when i do that the details are not shown properly
let nibName = UINib(nibName: "SubEventDetailCell", bundle: nil)
table.register(nibName, forCellReuseIdentifier: "TaskCell")
table.rowHeight = 200.00 // This was changed to 20
Update: I was messing around a bit with the code and updated this part
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
table.insertRows(at: [newIndexPath!], with: .fade)
case .delete:
table.deleteRows(at: [indexPath!], with: .fade)
case .update:
table.reloadData() // this was the change
case .move:
configureTable(table.cellForRow(at: indexPath!)! as! SubEventDetailCell, withEvent: anObject as! SubEvent, index: indexPath!.row)
default:
return
}
configureView()
}
This kinda fixed it for me, is it a good solution, can i keep it that way? or is there a better way to do it
Never retrieve cells from the table view force unwrapped. Don't do that. Be aware that a cell can be not on screen at the moment.
The most reliable way is to call only the methods to insert/delete/reload rows in the delegate method
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert: tableView.insertRows(at: [newIndexPath!], with: .automatic)
case .delete: tableView.deleteRows(at: [indexPath!], with: .fade)
case .update: tableView.reloadRows(at: [indexPath!], with: .none)
case .move:
tableView.deleteRows(at: [indexPath!], with: .automatic)
tableView.insertRows(at: [newIndexPath!], with: .fade)
}
}
and handle configure somewhere else for example inside cellForRowAt