当 UIViewController 被解除时重新加载 TableView?
Posted
技术标签:
【中文标题】当 UIViewController 被解除时重新加载 TableView?【英文标题】:Reloading TableView when a UIViewController is being dismissed? 【发布时间】:2018-08-21 17:58:21 【问题描述】:这里的问题是我在CommentVC
的当前上下文中以模态方式呈现EditCommentVC
,因为我想将UIView
的背景设置为半透明。现在,在EditCommentVC
上,我有一个UITextView
允许用户编辑他们的评论,还有2 个按钮-cancel
(关闭EditCommentVC
)和update
更新新评论并将其推送到数据库。
在代码方面,一切正常,除了一旦新评论被推送并且EditCommentVC
被关闭,CommentsVC
上的UITableView
和所有 cmets 都不会重新加载以显示更新的 cmets .尝试从viewWillAppear()
调用它,但它不起作用。
在这种情况下如何重新加载UITableView
中的数据?
@IBAction func updateTapped(_ sender: UIButton)
guard let id = commentId else return
Api.Comment.updateComment(forCommentId: id, updatedComment: editTextView.text!, onSuccess:
DispatchQueue.main.async
let commentVC = CommentVC()
commentVC.tableView.reloadData()
self.dismiss(animated: true, completion: nil)
, onError: error in
SVProgressHUD.showError(withStatus: error)
)
CommentVC
中的代码在其中转换(并传递注释的id
)。 CommentVC
符合 CommentActionProtocol
传递该评论的 id
:
extension CommentVC: CommentActionProtocol
func presentActionSheet(for commentId: String)
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let editAction = UIAlertAction(title: "Edit", style: .default) _ in
self.performSegue(withIdentifier: "CommentVCToEditComment", sender: commentId)
actionSheet.addAction(editAction)
present(actionSheet, animated: true, completion: nil)
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
if segue.identifier == "CommentVCToEditComment"
let editCommentVC = segue.destination as! EditCommentVC
let commentId = sender as! String
editCommentVC.commentId = commentId
【问题讨论】:
Api.Comment.updateComment
的回调是在后台线程中调用的吗?如果是这样,您可以将对 reloadData
的调用封装在 DispatchQueue.main.async
块中。
这是一个async
电话,是的。关于DispatchQueue.main.async
,我应该在viewWillAppear()
中这样做吗?因为我试过了,还是没有效果。
不,viewWillAppear
会在主线程上自动调用,无需在那里调度。我的意思是调度你在updateComment
的回调块中的代码,包括对reloadData
的注释掉调用,那是崩溃的。您还应该从主线程调用dismiss
。
我已经更新了 OP 中的代码,但仍然崩溃。错误状态Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
@Dani 你需要展示你的主视图控制器代码,尤其是你展示这个视图控制器的地方。
【参考方案1】:
我在这里看到至少 2 个问题:
您正在创建一个新 CommentVC
,如果您想在现有视图控制器中更新tableView
,则不应这样做。 p>
由于您提到Api.Comment.updateComment
是一个异步 调用,您需要编写UI 代码以在主线程 上运行。
所以首先你需要将commentVC
的实例放在这个viewController 的一个变量中。您可以从您展示此视图控制器的位置存储视图控制器的实例。
class EditCommentVC
var commentVCdelegate: CommentVC!
// Rest of your code
现在,当您呈现编辑视图控制器时,您需要在此变量中传递参考 commentVC。
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
if segue.identifier == "CommentVCToEditComment"
let editCommentVC = segue.destination as! EditCommentVC
let commentId = sender as! String
editCommentVC.commentId = commentId
editCommentVC.commentVCdelegate = self
现在你需要使用这个引用来重新加载你的 tableView。
Api.Comment.updateComment(forCommentId: id, updatedComment: editTextView.text!, onSuccess:
DispatchQueue.main.async
commentVCdelegate.tableView.reloadData() // - this commentVC must be an instance that you store of the your commentVC that you created the first time
self.dismiss(animated: true, completion: nil)
, onError: error in
SVProgressHUD.showError(withStatus: error)
)
【讨论】:
好的,我试过了。将let commentVC = CommentVC()
移到外面,但由于相同的错误而崩溃:Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
@Dani 我让你把它移到哪里去了?我说你需要从它首先创建的地方引用它。好吧,也许我不够清楚。一旦您完整显示其他视图控制器和此视图控制器的代码,我将编辑答案。
我猜我选词不当。它在EditCommentVC
的范围内
@Dani 你能在问题中添加你展示视图控制器的代码吗?
是的,我做到了 :) 我刚刚更新了它,以向您展示调用该代码的确切位置。它在UIAlertController
【参考方案2】:
嗯,我也有这个问题,我找到的解决方案是使用Protocol。我建议您搜索如何将数据发送回之前的 ViewController。这样,当您关闭EditCommentVC
时,然后将一个值(在我的情况下我发送 true)发送回 previous ViewController(在您的情况下,CommentVC) ,然后您将在 CommentVC 中有一个函数检查该值是否为 true,如果为 true,则重新加载 TableView。
在这里,让我给你看一个我是如何使用的例子(这些是我的 ViewControllers、函数和协议的名称,你可以使用任何你想要的东西并发送你想要的任何数据):
在您的 CommentVC 中,您将拥有如下内容:
protocol esconderBlurProtocol
func isEsconder(value: Bool)
class PalestranteVC: UIViewController,esconderBlurProtocol
func isEsconder(value: Bool)
if(value)
//here is where you can call your api again if you want and reload the data
tableView.reloadData()
另外,不要忘记您必须设置 EditCommentVC 的委托,所以在您呈现 EditCommentVC 时这样做,如下所示:
let viewController = (self.storyboard?.instantiateViewController(withIdentifier: "DetalhePalestranteVC")) as! DetalhePalestranteVC
viewController.modalPresentationStyle = .overFullScreen
viewController.delegate = self
self.present(viewController, animated: true, completion: nil)
//replace **DetalhePalestranteVC** with your **EditCommentVC**
在你的 EditCommentVC 中你会有这样的东西:
class DetalhePalestranteVC: UIViewController
var delegate: esconderBlurProtocol?
override func viewWillDisappear(_ animated: Bool)
delegate?.isEsconder(value: true)
这样,您关闭 EditCommentVC 的所有内容,您将返回 True 并重新加载 tableView。
【讨论】:
"然后您将一个值(在我的情况下我发送 true)发送回前一个 ViewController(在您的情况下为 CommentVC),然后您将在 ViewWillAppear 中的 CommentVC 中拥有一个函数检查该值是否为真,如果是,则重新加载 TableView" 这听起来已经是一个糟糕的方法了。 @RakeshaShastri 你能解释一下为什么它听起来像一个糟糕的方法吗? tableView 应该在满足特定条件时重新加载。因此,执行此操作的正确位置是满足条件的地方。 viewWillAppear 用于在视图即将出现时需要做的事情。在另一个视图控制器上满足某些条件时重新加载 tableView 不是其中之一。您为此使用委托。除非您需要在多个地方进行更改,在这种情况下您使用通知。我并不是说你的回答不能解决问题,很可能,但这不是一个好方法。 viewWillAppear 上没有调用它,这是我输入的错误。它在前一个 ViewController 调用协议并将数据发送回该 Controller 时调用。 你做的和我一样。传递父视图的引用,但不同之处在于您将其隐藏在协议后面。以上是关于当 UIViewController 被解除时重新加载 TableView?的主要内容,如果未能解决你的问题,请参考以下文章
调用解除方法后如何正确重新加载 UIViewController?
检测 UIAlertController 何时被另一个 UIViewController 解除