如何重新加载视图控制器,以便更新JSON?

时间:2019-01-14 17:48:08

标签: ios json swift uitableview

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

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()
    }
}

2 个答案:

答案 0 :(得分:0)

首先,从不永远调用自己的委托方法,这些方法包含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()

答案 1 :(得分:0)

虽然您同时调用-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)

同样,我强烈建议至少提取提取并删除API逻辑。

对于执行删除操作并使用户向其显示记录已删除的方式,您还有几种选择。

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

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

由于前一种方法会受到连接和网络延迟问题的困扰,因此当用户尝试删除记录时,它可能对用户来说确实很滞后,并且您失去了利用UITableView动画的能力,该动画使您清楚一行已经被删除了。删除。

后一种方法将对用户更加敏感。对于这种方法,您需要从当前调用-viewDidLoad-viewWillAppear:然后调用remove the corresponding row的数组中删除已删除的对象。像这样:

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

您可以选择animation types

乐观地在应用中本地删除记录和行的一个挑战是,如果API请求由于某种原因失败,那么下次获取数据模型时,用户体验可能会不一致-就像记录一样未删除(实际上是这种情况)。因此,根据一致性的重要性以及您需要执行的错误处理量,您将需要协调删除请求中出现的所有错误。同样,您还有很多选择。您可以在后台再次尝试删除,也可以提醒用户,然后重新添加行,以便他们可以再次尝试,或者什么也不做。