【问题标题】:How do I reload my view controller so that my JSON updates?如何重新加载我的视图控制器以便我的 JSON 更新?
【发布时间】:2019-06-08 17:54:46
【问题描述】:

我正在将一些 JSON 数据加载到 UITableView 中。每个单元格都有一个删除按钮,当按下此按钮时,对象会成功从服务器中删除。但是,该页面不会更新以反映此更改。我希望重新加载表视图但没有删除的对象。我试过调用 viewDidLoad() 和 viewDidAppear() 以及我在网上找到的其他几个技巧。目前,我正在使用一种将用户发送回主页的解决方法。从那里,当您单击带有表格视图的页面时,它已更新以反映更改。但是,如果不离开视图控制器并返回它,我似乎无法做到这一点。我知道 API 调用工作正常,只是页面没有“刷新”。我应该怎么做才能使这项工作? 非常感谢!我的整个班级的代码如下(我添加了几个 cmets 来指出我遇到问题的地方)

import UIKit

class Garage: UIViewController, UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.vehicles.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as UITableViewCell
        cell.textLabel?.text = vehicles[indexPath.row].year + "   " + vehicles[indexPath.row].make + "   " + vehicles[indexPath.row].model
        let button = UIButton(type: .custom)
        button.backgroundColor = UIColor.green
        button.sizeToFit()
        cell.accessoryType = .detailButton
        return cell
    }

    func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
        print("tapped")
        let alert = UIAlertController(title: "Would you like to delete this vehicle?", message: "This action cannot be undone.", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: nil))

        self.present(alert, animated: true)
        alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { action in
            print("DELETED")
            //
            //THIS IS WHERE THE DELETE ACTION IS CALLED
            //
            var id = self.vehicles[indexPath.row].id
            print("got here 1")

            let api = "https://api.myapi.com/"
            let parameters: [String: String] = ["id": id]
            guard let url = URL(string: api + id) else { return }
            var request = URLRequest(url: url)

            let headers: [String: String] = [
                "Content-Type": "application/json"
            ]
            request.allHTTPHeaderFields = headers
            request.httpMethod = "DELETE"
            let requestBody = try? JSONSerialization.data(withJSONObject: parameters, options: [])
            if let requestBody = requestBody {
                request.httpBody = requestBody
            }
            //
            //THIS IS WHERE I TRY TO RELOAD THE PAGE
            //
            print("reloading...")
            self.viewDidLoad()
            self.viewWillAppear(true)
            URLSession.shared.dataTask(with: request) { (data, response, error) in
                // print("Data, response, error", data, response, error)
                if let data = data {
                    let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] ?? [:]
                    print("json", json)
                }
                DispatchQueue.main.async {
                    self.tableVehicles.reloadData()
                    self.view.setNeedsLayout()
                    self.performSegue(withIdentifier: "GarageToHome", sender: self)
                }

                }.resume()
            //
            //THIS IS MY CURRENT WORK-AROUND
            //
            self.performSegue(withIdentifier: "GarageToHome", sender: self)
        }))
    }

    @IBOutlet weak var label1: UILabel!

    struct Vehicle {
        var make: String
        var model: String
        var year: String
        var Trim: String
        var id: String
        init(_ dictionary: [String: Any]) {
            self.make = dictionary["make"] as? String ?? ""
            self.model = dictionary["model"] as? String ?? ""
            self.year = dictionary["year"] as? String ?? ""
            self.Trim = dictionary["trim"] as? String ?? ""
            self.id = dictionary["id"] as? String ?? ""
        }
    }

    var vehicles = [Vehicle]()
    @IBOutlet weak var tableVehicles: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // print("login name:", username)
        self.tableVehicles.delegate = self
        self.tableVehicles.dataSource = self
        self.tableVehicles.reloadData()

    }

    override func viewWillAppear(_ animated: Bool) {
        //vehicles = [Vehicle]()
        let defaults = UserDefaults.standard
        defaults.synchronize()
        let token = UserDefaults.standard.string(forKey: "token")
        let isSignedIn = UserDefaults.standard.bool(forKey: "isUserLoggedIn")
        var username = UserDefaults.standard.string(forKey: "loginName")
        defaults.synchronize()
        DispatchQueue.main.async {
            self.label1.text = "Welcome, " + username! + "!"
            defaults.synchronize()
        }

        guard let url = URL(string: "https://api.myapi.com") else {return}
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let dataResponse = data,
                error == nil else {
                    print(error?.localizedDescription ?? "Response Error")
                    return }
            do{
                let jsonResponse = try JSONSerialization.jsonObject(with:
                    dataResponse, options: [])
                //print(jsonResponse) //Response result
                guard let jsonArray = jsonResponse as? [[String: Any]] else {
                    return
                }
                for dic in jsonArray{
                    self.vehicles.append(Vehicle(dic))
                }
                print(self.vehicles)
                DispatchQueue.main.async {
                    self.tableVehicles.delegate = self
                    self.tableVehicles.dataSource = self
                    self.tableVehicles.reloadData()
                }
            } catch let parsingError {
                print("Error", parsingError)
            }
        }
        task.resume()
    }
}

【问题讨论】:

  • 您需要做的就是刷新显示数据的对象。无需释放整个视图控制器(以及其中包含的所有对象)并重新初始化它;这是矫枉过正。显示您的数据的对象是UITableView,并且与您首次加载该表的方式大致相同,您将如何重新加载它。但是,为避免异常错误,您应该考虑使用两个数组(一个用于所有解析转储到一个,另一个由表的数据源用于填充单元格)。

标签: ios json swift uitableview


【解决方案1】:

首先,永远不要自己调用包含willdidshould 的委托方法。不要那样做。这些方法仅由框架调用。

有一个非常简单的解决方案。您知道已删除行的索引路径,因此如果数据任务成功,则从数据源数组中删除项目并删除表视图中的行。不需要重新加载整个表视图并从下载的数据中更新数据源数组。

反正需要一个明确的指标表明服务器操作成功,不知道dataTask中的if let data = data够不够用。

//THIS IS WHERE I TRY TO RELOAD THE PAGE
//
print("reloading...")
URLSession.shared.dataTask(with: request) { (data, response, error) in
   // print("Data, response, error", data, response, error)
   if let data = data {
      let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] ?? [:]
      print("json", json)
      DispatchQueue.main.async {
         self.vehicles.remove(at: indexPath.row)
         tableView.deleteRows(at: [indexPath], with: .fade)
         self.performSegue(withIdentifier: "GarageToHome", sender: self)
      }
   }
}.resume()

【讨论】:

    【解决方案2】:

    当您同时调用 -viewDidLoad-viewWillAppear(它们正在重新加载您的表格视图并执行您的 API 请求以分别获取您的数据和填充您的模型)时,您可能看不到您的底层数据模型的一个原因似乎是更新是您在实际尝试将删除请求发送到您的 API 端点之前调用两者。如果您将对 -viewWillAppear 的调用移至删除请求的完成块,那么这将确保您从 API 获取更新数据的请求在删除完成后发生。

    这是你可以做的最起码的事情,可以让这个工作像你期望的那样工作,但是我强烈建议你花一点时间来考虑你的业务和网络逻辑。您不应该自己手动调用-viewDidLoad-viewWillAppear。这些是 UIViewController 生命周期方法,在视图控制器的生命周期中由 UIKit 为您调用。相反,我建议(至少)将一些方法或方法分解到其他类中,以处理从服务器获取数据模型或删除特定记录等事情。然后,您可以从表视图委托或UIAlertAction 以更可重用和隔离的方式调用这些方法,而不必担心调用 UIViewController 生命周期方法只是为了再次获取一些数据而产生的任何意外副作用。

    所以,虽然我真的不建议你最终使用这个作为你的最终代码,而不是这个:

    //THIS IS WHERE I TRY TO RELOAD THE PAGE
    //
    print("reloading...")
    self.viewDidLoad()
    self.viewWillAppear(true)
    URLSession.shared.dataTask(with: request) { (data, response, error) in
    

    你可以这样做:

    URLSession.shared.dataTask(with: request) { (data, response, error) in
        //THIS IS WHERE I TRY TO RELOAD THE PAGE
        //
        print("reloading...")
        self.viewDidLoad()
        self.viewWillAppear(true)
    

    再次,我强烈建议,至少,提取您的 fetch 和 delete API 逻辑。

    对于如何执行删除操作并让用户看到记录已被删除,您还有多种选择。

    • 如上所述,对您所做的最小更改只是为了确保仅在删除请求成功后才获取更新的数据模型。

    • 或者,您可以通过在本地更新支持数据模型并删除受影响的行或重新加载表来乐观地使您的删除请求看起来成功。

    由于前一种方法会受到连接和网络延迟问题的影响,因此当用户尝试删除记录时,它可能会显得非常滞后,并且您无法利用 UITableView 动画来清楚地表明一行已被删除已删除。

    后一种方法对用户的响应会更好。对于这种方法,您需要从当前调用-viewDidLoad-viewWillAppear: 然后remove the corresponding row 的数组中删除已删除的对象。比如:

    self.vehicles.removeAt(indexPath.row)
    tableView.deleteRows(at: [indexPath], with: .automatic)
    

    您可以选择animation types

    在您的应用中本地删除记录和行的一个挑战是,如果 API 请求由于某种原因失败,那么您的用户体验可能会在下次获取您的数据模型时出现不一致 - 它会显示为好像记录没有被删除(实际上是这样)。因此,根据一致性的重要性以及您需要处理多少错误,您需要协调作为删除请求的一部分出现的任何错误。在这里,你有很多选择。您可以在后台再次尝试删除,您可以提醒您的用户,然后重新添加该行,以便他们可以重试,或者什么也不做。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-07
      • 2015-09-21
      • 1970-01-01
      • 1970-01-01
      • 2019-09-09
      • 1970-01-01
      • 2014-09-11
      • 1970-01-01
      相关资源
      最近更新 更多