当 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 解除

UIViewController 在解除先前呈现的模态视图控制器后被释放

链接 uiviewcontroller 委托

从UINavigationController中解除具有自定义转换的UIViewController时出现黑屏