Как перезагрузить контроллер представления, чтобы обновления JSON были? - PullRequest
0 голосов
/ 14 января 2019

Я загружаю некоторые данные 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 голосов
/ 14 января 2019

Когда вы звоните и -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:, а затем удалить соответствующую строку . Что-то вроде:

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

У вас есть выбор типов анимации .

Одна из проблем оптимистичного удаления записи и строки локально в вашем приложении состоит в том, что если запрос API по какой-то причине не выполняется, то при следующем извлечении вашей модели данных ваш пользовательский интерфейс может показаться непоследовательным - это будет выглядеть так, как будто запись не был удален (что на самом деле так). Таким образом, в зависимости от важности согласованности и объема обработки ошибок, который вам необходимо выполнить, вам нужно будет согласовать любые ошибки, возникающие как часть вашего запроса на удаление. Здесь снова у вас есть много вариантов. Вы можете попробовать удалить снова за кулисами, вы можете предупредить пользователя, а затем добавить строку назад, чтобы он мог повторить попытку или просто ничего не делать.

0 голосов
/ 14 января 2019

Прежде всего, никогда не вызывает методы делегата, содержащие will, did и should самостоятельно. Не делай этого. Эти методы вызываются исключительно фреймворком.

Существует довольно простое решение. Вы знаете путь индекса удаленной строки, поэтому, если задача с данными успешно выполняется, удалите элемент из массива источника данных и удалите строку в табличном представлении. Перезагрузка всего табличного представления и обновление массива источника данных из загруженных данных не требуется.

В любом случае вам нужен четкий индикатор того, что работа сервера прошла успешно, я не знаю, достаточно ли if let data = data в dataTask.

//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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...