从 UIAlertController 重新加载 tableView 数据不起作用
Posted
技术标签:
【中文标题】从 UIAlertController 重新加载 tableView 数据不起作用【英文标题】:Reload tableView data from UIAlertController not working 【发布时间】:2017-03-09 12:28:00 【问题描述】:我在从 UIAlertController
刷新 tableView 数据时遇到问题。
该代码适用于测验式应用程序,此页面允许用户选择
主题以及其他一些选项(请参阅屏幕截图)。有一个
“仅显示未见过的问题”旁边的重置按钮会触发
UIAlertController
。但是,单击此警报中的重置操作
更新数据库但不更新 tableView。数据库是
肯定更新了,好像我返回一个页面然后重新访问
tableView,主题单元格中看不见的问题值被更新。我意识到有很多
这些类型的问题在这里,但恐怕没有一个通常的修复方法
工作。
额外信息:
tableView是用一系列自定义的 UITableViewCells 通过 FMDB 从 SQLite 数据库加载数据 UIAlertController 在重置时从 NSNotification 触发 按钮被点击到目前为止我有:
检查的数据源和委托设置正确,以编程方式和 在IB。通过print(self.tableView.datasource)
等确认
已确认 reloadData()
正在解雇
为reloadData()
使用主线程
下面是 TableViewController 代码和屏幕截图的摘录。
override func viewDidLoad()
super.viewDidLoad()
self.tableView.dataSource = self
self.tableView.delegate = self
//For unique question picker changed
NotificationCenter.default.addObserver(self, selector: #selector(SubjectsTableViewController.reloadView(_:)), name:NSNotification.Name(rawValue: "reload"), object: nil)
//For slider value changed
NotificationCenter.default.addObserver(self, selector: #selector(SubjectsTableViewController.updateQuantity(_:)), name:NSNotification.Name(rawValue: "updateQuantity"), object: nil)
//Trigger UIAlertController
NotificationCenter.default.addObserver(self, selector: #selector(SubjectsTableViewController.showAlert(_:)), name:NSNotification.Name(rawValue: "showAlert"), object: nil)
// MARK: - Table view data source
///////// Sections and Headers /////////
override func numberOfSections(in tableView: UITableView) -> Int
return 3
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
let subjectHeaderCell = tableView.dequeueReusableCell(withIdentifier: "sectionHeader")
switch section
case 0:
subjectHeaderCell?.textLabel?.text = "Select Subjects"
return subjectHeaderCell
case 1:
subjectHeaderCell?.textLabel?.text = "Options"
return subjectHeaderCell
case 2:
subjectHeaderCell?.textLabel?.text = ""
return subjectHeaderCell
default:
subjectHeaderCell?.textLabel?.text = ""
return subjectHeaderCell
//Header heights
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
return 34.0
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
switch section
case 0:
return SubjectManager.subjectWorker.countSubjects()
case 1:
return 2
case 2:
return 1
default:
return 0
///////// Rows within sections /////////
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
switch (indexPath.section)
case 0:
//Configure subjectCell //
let cellWithSubject = tableView.dequeueReusableCell(withIdentifier: "subjectCell", for: indexPath) as! SubjectTableViewCell
//Curve corners
cellWithSubject.subjectCellContainer.layer.cornerRadius = 2
cellWithSubject.subjectCellContainer.layer.masksToBounds = true
//Set subject title label
cellWithSubject.subjectTitleLabel.text = SubjectManager.subjectWorker.collateSubjectTitles()[indexPath.row]
//Available questions for subject label
questionCountForSubjectArray = QuestionManager.questionWorker.countQuestions()
cellWithSubject.subjectAvailableQuestionsLabel.text = "Total questions available: \(questionCountForSubjectArray[indexPath.row])"
//Get questions in subject variables
seenQuestionsForSubjectArray = QuestionManager.questionWorker.countOfQuestionsAlreadySeen()
//New questions available label
unseenQuestionsForSubjectArray.append(questionCountForSubjectArray[indexPath.row] - seenQuestionsForSubjectArray[indexPath.row])
cellWithSubject.newQuestionsRemainingLabel.text = "New questions remaining: \(unseenQuestionsForSubjectArray[indexPath.row])"
return cellWithSubject
case 1:
switch (indexPath.row)
case 0:
//Configure uniqueQuestionCell //
let cellWithSwitch = tableView.dequeueReusableCell(withIdentifier: "uniqueQuestionCell", for: indexPath) as! UniqueQuestionTableViewCell
//Curve corners
cellWithSwitch.uniqueQuestionContainer.layer.cornerRadius = 2
cellWithSwitch.uniqueQuestionContainer.layer.masksToBounds = true
return cellWithSwitch
case 1:
//Configure sliderCell //
let cellWithSlider = tableView.dequeueReusableCell(withIdentifier: "questionPickerCell", for: indexPath) as! QuestionPickerTableViewCell
//Curve corners
cellWithSlider.pickerCellContainer.layer.cornerRadius = 2
cellWithSlider.pickerCellContainer.layer.masksToBounds = true
//Set questions available label
cellWithSlider.questionsAvailableLabel.text = "Available: \(sumQuestionsSelected)"
//Configure slider
cellWithSlider.questionPicker.maximumValue = Float(sumQuestionsSelected)
cellWithSlider.questionPicker.isContinuous = true
//Logic for if available questions changes - updates slider stuff
if questionQuantityFromSlider > sumQuestionsSelected
questionQuantityFromSlider = sumQuestionsSelected
cellWithSlider.questionsToStudy = questionQuantityFromSlider
cellWithSlider.questionsChosenLabel.text = "Questions to study: \(questionQuantityFromSlider)"
else questionQuantityFromSlider = cellWithSlider.questionsToStudy
//Configure questions chosen label:
if questionsToStudyDict.isEmpty
cellWithSlider.chooseSubjectsLabel.text = "Choose a subject"
cellWithSlider.questionsChosenLabel.text = "Questions to study: 0"
else
cellWithSlider.chooseSubjectsLabel.text = ""
return cellWithSlider
default:
return UITableViewCell()
case 2:
print("cellForRowAt case 2")
//Configure beginCell //
let cellWithStart = tableView.dequeueReusableCell(withIdentifier: "beginCell", for: indexPath) as! BeginStudyTableViewCell
//Curve corners
cellWithStart.startContainer.layer.cornerRadius = 2
cellWithStart.startContainer.layer.masksToBounds = true
return cellWithStart
default:
return UITableViewCell()
//Row heights
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
switch (indexPath.section)
case 0:
return 120.0
case 1:
switch (indexPath.row)
case 0:
return 60.0
case 1:
return 100.0
default:
return 44.0
case 2:
return 100.0
default:
return 44.0
override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool
if indexPath.section == 2 || indexPath.section == 0
return true
else
return false
override func tableView(_ tableView: UITableView, didHighlightRowAt indexPath: IndexPath)
if indexPath.section == 2 && selectedRowsDict.isEmpty != true && questionQuantityFromSlider > 0
let cellToBegin = tableView.cellForRow(at: indexPath) as! BeginStudyTableViewCell
cellToBegin.startContainer.backgroundColor = UIColor.lightGray
override func tableView(_ tableView: UITableView, didUnhighlightRowAt indexPath: IndexPath)
if indexPath.section == 2
let cellToBegin = tableView.cellForRow(at: indexPath) as! BeginStudyTableViewCell
cellToBegin.startContainer.backgroundColor = UIColor.white
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
switch (indexPath.section)
case 0:
//Set checkbox to ticked image
let cellWithSubject = tableView.cellForRow(at: indexPath) as! SubjectTableViewCell
cellWithSubject.subjectSelectedImageView.image = UIImage(named: "CheckboxTicked")
//Determine questions available for subject depending on unseen value
if showUnseenQuestions == true
questionsToStudyDict[indexPath.row] = unseenQuestionsForSubjectArray[indexPath.row]
else
questionsToStudyDict[indexPath.row] = questionCountForSubjectArray[indexPath.row]
//Sum questions available
sumQuestionsSelected = Array(questionsToStudyDict.values).reduce(0, +)
//Reload table to pass this to questions available label in UISlider cell and reselect selected rows
let key: Int = indexPath.row
selectedRowsDict[key] = indexPath.row
self.tableView.reloadData()
if selectedRowsDict.isEmpty == false
for (keys,_) in selectedRowsDict
let index: IndexPath = NSIndexPath(row: selectedRowsDict[keys]!, section: 0) as IndexPath
tableView.selectRow(at: index, animated: false, scrollPosition: .none)
case 1:
break
case 2:
if selectedRowsDict.isEmpty != true && questionQuantityFromSlider > 0
self.performSegue(withIdentifier: "showStudyQuestion", sender: self)
else
print("Segue not fired")
default:
break
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath)
if indexPath.section == 0
//Set checkbox to unticked image
let cellWithSubject = tableView.cellForRow(at: indexPath) as! SubjectTableViewCell
cellWithSubject.subjectSelectedImageView.image = UIImage(named: "Checkbox")
//Remove questions available for unselected subject from questions dictionary
questionsToStudyDict[indexPath.row] = nil
//Update sum of questions selected
sumQuestionsSelected = Array(questionsToStudyDict.values).reduce(0, +)
//Reload table to pass this to questions available label in UISlider cell and reselect selected rows
let key: Int = indexPath.row
selectedRowsDict[key] = nil
self.tableView.reloadData()
if selectedRowsDict.isEmpty == false
for (keys,_) in selectedRowsDict
let index: IndexPath = NSIndexPath(row: selectedRowsDict[keys]!, section: 0) as IndexPath
tableView.selectRow(at: index, animated: false, scrollPosition: .none)
func reloadView(_ notification: Notification)
//Change bool value
showUnseenQuestions = !showUnseenQuestions
//For keys in dict, update values according to showUnseenQuestion value
if showUnseenQuestions == true
for (key,_) in questionsToStudyDict
questionsToStudyDict[key] = unseenQuestionsForSubjectArray[key]
else
for (key,_) in questionsToStudyDict
questionsToStudyDict[key] = questionCountForSubjectArray[key]
//Re-run sum dict function
sumQuestionsSelected = Array(questionsToStudyDict.values).reduce(0, +)
//Finally reload the view and reselect selected rows
let selectedRowsIndexes = tableView.indexPathsForSelectedRows
self.tableView.reloadData()
if selectedRowsIndexes != nil
for i in (selectedRowsIndexes)!
tableView.selectRow(at: i, animated: false, scrollPosition: .none)
func updateQuantity(_ notification: Notification)
//Reload the view and reselect selected rows
let selectedRowsIndexes = tableView.indexPathsForSelectedRows
self.tableView.reloadData()
if selectedRowsIndexes != nil
for i in (selectedRowsIndexes)!
tableView.selectRow(at: i, animated: false, scrollPosition: .none)
func showAlert(_ notification: Notification)
let alertController = UIAlertController(title: "Reset Seen Questions", message: "Are you sure you want to reset all questions to unseen?", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) action in
// ...
alertController.addAction(cancelAction)
let OKAction = UIAlertAction(title: "Reset", style: .default, handler:(action:UIAlertAction) -> Void in
QuestionManager.questionWorker.resetHasSeenValues()
self.reloadData()
print("reloadData fired")
)
alertController.addAction(OKAction)
self.present(alertController, animated: true, completion: nil)
func reloadData()
DispatchQueue.main.async(execute:
self.tableView.reloadData()
)
func countOfQuestionsAlreadySeen() -> [Int]
var questionSeenYesArray: [Int] = []
if openWriteDatabase()
let queryYes = "SELECT SUM(hasSeen) FROM UserData GROUP BY subjectID"
let querySeenYes: FMResultSet? = writeDatabase?.executeQuery(queryYes, withArgumentsIn: nil)
while (querySeenYes?.next())!
if let questionSeenYes = (querySeenYes?.int(forColumnIndex: 0))
questionSeenYesArray.append(Int(questionSeenYes))
return questionSeenYesArray
func resetHasSeenValues()
if openWriteDatabase()
let resetHasSeenValues = "UPDATE UserData Set hasSeen = 0"
_ = writeDatabase?.executeUpdate(resetHasSeenValues, withArgumentsIn: nil)
【问题讨论】:
你写“数据库肯定更新了,就像我回到一个页面然后重新访问tableView,主题单元格中看不见的问题值被更新了。”。您的QuestionManager
是否异步重置所见值?
@Nathan 我不完全确定。 QuestionManager
是我用来保存一堆函数的单例。我已经用countOfQuestionsAlreadySeen()
函数更新了原始帖子,该函数应该在执行reloadData()
时执行,如果有帮助吗?
更有趣的是看看QuestionManager.questionWorker.resetHasSeenValues()
做了什么。也许这就是你可以开始调试的地方?
@Nathan - 抱歉,我本来是想把它发给你的! OP 已更新。我刚刚检查过 - 当我解除警报并保持在同一页面上(没有重新访问)时,resetHasSeenValues()
肯定会正确更新数据库。看来我需要研究同步/异步操作...
我相信您,数据库已正确更新。但我不确定,数据库是否按时更新。我试图制作您示例的简化版本(没有数据库)并且一切正常。这就是我假设存在同步/异步问题的原因。你可以找到我的代码here。
【参考方案1】:
更多调试显示unseenQuestionsForSubjectArray
没有在cellForRowAt
方法中正确填充。我解决了这个问题,这解决了reloadData()
问题。谢谢大家的帮助。
【讨论】:
【参考方案2】:在主队列中添加self.reloadData()
。出口更改始终应保存在主线程内。
let OKAction = UIAlertAction(title: "Reset", style: .default, handler:(action:UIAlertAction) -> Void in
QuestionManager.questionWorker.resetHasSeenValues()
DispatchQueue.main.async
self.tableView.reloadData()
print("reloadData fired")
)
【讨论】:
如前所述,我已经尝试过了,但没有运气。还是谢谢!以上是关于从 UIAlertController 重新加载 tableView 数据不起作用的主要内容,如果未能解决你的问题,请参考以下文章
注册通知时如何从存在的 UIAlertcontroller 获取 UIAlertAction
尝试在解除分配时加载视图控制器的视图... UIAlertController
使用 StoreKit 时获取 Apple UIAlertController 完成
从 AppDelegate 到 PresentedViewController 的警报:“尝试在...上呈现 UIAlertController 已经在呈现 UIAlertController”