Проблема с загрузкой данных из массива в ячейки UITableView

#ios #swift #xcode #uitableview

#iOS #swift #xcode #uitableview

Вопрос:

Я очень новичок в программировании swift и пытаюсь создать приложение для приема заказов и передачи их в приложение администратора. Мои данные не загружаются в мой UITableView, и я не уверен, почему, насколько я могу судить, я все сделал по книге. Я загружаю данные с созданного мной сервера узлов, и при печати содержимого массива все элементы печатаются как значения ключа, пары. UIImages загружаются в каждую из ячеек TableView, но метки — нет, и после установки меток и их печати значения меток по-прежнему равны нулю.

Я создал класс TableView с именем PizzaListTableViewController и class вызываемую PizzaTableViewCell пользовательскую ячейку tableViewCell. Я добавил UIImage и три метки в конструктор интерфейса раскадровки.

Структура такова: ViewController > TableView > tableViewCell > Изображение, метки

Мой основной VC подключен к его ViewController.class Моя tableViewCell подключена к его TableViewCell.class У меня есть идентификатор, и я связал его, согласно приведенному ниже коду, я связал все выходы. Любая помощь будет высоко оценена!

Я попытался переписать классы, разорвать все подключения к розеткам и повторно подключить их, присвоить значения в методе, в котором установлены метки, но ни с чем не повезло.

 class PizzaListTableViewController: UITableViewController {
    var pizzas: [Pizza] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        //title you will see on the app screen at the top of the table view
        navigationItem.title = "Drink Selection"

        //tableView.estimatedRowHeight = 134
        //tableView.rowHeight = UITableViewAutomaticDimension

        fetchInventory { pizzas in
            guard pizzas != nil else { return }
            self.pizzas = pizzas!
            //print(self.pizzas)
            self.tableView.reloadData()
            //print(self.pizzas)
        }


    }   //end of viewDidLoad

    private func fetchInventory(completion: @escaping ([Pizza]?) -> Void) {
        Alamofire.request("http://127.0.0.1:4000/inventory", method: .get)
            .validate()
            .responseJSON { response in
                guard response.result.isSuccess else { return completion(nil) }
                guard let rawInventory = response.result.value as? [[String: Any]?] else { return completion(nil) }
                let inventory = rawInventory.compactMap { pizzaDict -> Pizza? in
                    var data = pizzaDict!
                    data["image"] = UIImage(named: pizzaDict!["image"] as! String)

                    //print(data)
                    //print("CHECK")
                    print("Printing all data: ", Pizza(data: data))
                    //printing all inventory successful


                    return Pizza(data: data)
                }
                //self.tableView.reloadData()
                completion(inventory)
        }
    }

    @IBAction func ordersButtonPressed(_ sender: Any) {
        performSegue(withIdentifier: "orders", sender: nil)
    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    //PRINTING ROWS 0 TWICE in console
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        //print("ROWS", pizzas.count)
        return self.pizzas.count
    }


    //THIS IS WHERE THE CELL IDENTIFIER IS ??
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //print("IN CELLFORROWAT")

        tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "cell")

        let cell: PizzaTableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! PizzaTableViewCell

        //cell.backgroundColor = Services.baseColor

        cell.name?.text = pizzas[indexPath.row].name
        cell.imageView?.image = pizzas[indexPath.row].image
        cell.amount?.text = "$(pizzas[indexPath.row].amount)"
        cell.miscellaneousText?.text = pizzas[indexPath.row].description

        print(cell.name?.text! as Any)
        print(cell.imageView as Any)
        //print("END CELLFORROWAT")

        return cell
    }

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100.0
    }  //END OF

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        performSegue(withIdentifier: "pizza", sender: self.pizzas[indexPath.row] as Pizza)
    }  //END OF override func tableView

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "pizza" {
            guard let vc = segue.destination as? PizzaViewController else { return }
            vc.pizza = sender as? Pizza
        }
    }  //END OF override preppare func

}
  
 class PizzaTableViewCell: UITableViewCell {

    @IBOutlet weak var name: UILabel!
    @IBOutlet weak var pizzaImageView: UIImageView!
    @IBOutlet weak var amount: UILabel!

    @IBOutlet weak var miscellaneousText: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

         //Configure the view for the selected state
    }

}
  
 struct Pizza {
    let id: String
    let name: String
    let description: String
    let amount: Float
    let image: UIImage

    init(data: [String: Any]) {

        //print("CHECK:: pizza.swift")

        self.id = data["id"] as! String
        self.name = data["name"] as! String

//        self.amount = data["amount"] as! Float
        self.amount = ((data["amount"] as? NSNumber)?.floatValue)!

        self.description = data["description"] as! String
        self.image = data["image"] as! UIImage
    }

}
  

Я также напечатал значения массива на консоль, и данные печатаются, как ожидалось, но значения cell.name?.text , cell.amount?.text и cell.miscellaneousText?.text печатают ноль.

Комментарии:

1. Я вижу несколько вещей… tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "cell") следует вызывать только один раз и делать это в viewDidLoad. Поместите печать cell.name ? .text = pizzas[indexPath.row].name и посмотрите, соответствует ли pizzas[indexPath.row].name вашим ожиданиям.

2. Я пробовал это раньше, кажется, что ввод tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "cell") в viewDidLoad или там, где у меня есть это выше, не помогает :/

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

4. Если ячейка спроектирована как ячейка прототипа в Interface Builder, вы не должны регистрировать ячейку. И почему вы используете, compactMap хотя никогда не бывает nil случая?

5. Я спроектировал ячейку в Interface builder, я попробую удалить оператор register в коде. Ранее у меня было let inventory = rawInventory.flatMap , но xocde представил предупреждение об исправлении / изменении его на то, что у меня есть; это неправильный способ сделать это?

Ответ №1:

Пожалуйста, попробуйте перезагрузить свой tableview в основном потоке внутри кода, который вы передаете в качестве параметра в fetchInventory:

 DispatchQueue.main.async { 
    self.tableView.reloadData()
}
  

Итак, ваш вызов fetchInventory должен стать:

     fetchInventory { pizzas in
        guard pizzas != nil else { return }
        self.pizzas = pizzas!
        //print(self.pizzas)
        DispatchQueue.main.async { 
            self.tableView.reloadData()
        }
        //print(self.pizzas)
    }
  

Пожалуйста, избегайте выполнения работы пользовательского интерфейса из фонового потока, потому что это неправильно / безопасно. Кроме того, вы можете попробовать установить self?.pizzas тоже внутри этого блока основного потока.
И, пожалуйста, примите во внимание совет Алана по двойному вызову.

  1. Пожалуйста, полностью удалите регистр из TableView / cellForRow.

     // tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "cell")
      
  2. Вместо:

     cell.imageView?.image = pizzas[indexPath.row].image
      

    поместить:

     cell.pizzaImageView?.image = pizzas[indexPath.row].image
      

Это имя вашей торговой точки.

Пожалуйста, проверьте мой тест ниже, который работаетвведите описание изображения здесь:

 import UIKit
  

класс PizzaListTableViewController: UITableViewController {

 var pizzas: [Pizza] = []

override func viewDidLoad() {
    super.viewDidLoad()
    //title you will see on the app screen at the top of the table view
    navigationItem.title = "Drink Selection"

    //tableView.estimatedRowHeight = 134
    //tableView.rowHeight = UITableViewAutomaticDimension

    fetchInventory { pizzas in
        guard pizzas != nil else { return }
        self.pizzas = pizzas!
        print(self.pizzas)
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
        //print(self.pizzas)
    }
}   //end of viewDidLoad

private func fetchInventory(completion: @escaping ([Pizza]?) -> Void) {
    let rawInventory0 = [
        [
            "id": "1",
            "name": "name1",
            "amount": 1234,
            "description": "description1",
            "image": "image1"
        ],
        [
            "id": "2",
            "name": "name2",
            "amount": 1235,
            "description": "description2",
            "image": "image2"
        ],
        [
            "id": "3",
            "name": "name3",
            "amount": 1236,
            "description": "description3",
            "image": "image3"
        ],
        [
            "id": "4",
            "name": "name4",
            "amount": 1237,
            "description": "description4",
            "image": "image4"
        ]
    ]  as? [[String: Any]?]
    guard let rawInventory1 = rawInventory0 as? [[String: Any]?] else { return completion(nil) }
    let inventory = rawInventory1.compactMap { pizzaDict -> Pizza? in
        var data = pizzaDict!
        data["image"] = UIImage(named: pizzaDict!["image"] as! String)
        print(data)
        print("CHECK")
        print("Printing all data: ", Pizza(data: data))
        //printing all inventory successful
        return Pizza(data: data)
    }
    //self.tableView.reloadData()
    completion(inventory)
}

// MARK: - Table view data source

@IBAction func ordersButtonPressed(_ sender: Any) {
    performSegue(withIdentifier: "orders", sender: nil)
}

override func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

//PRINTING ROWS 0 TWICE in console
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    //print("ROWS", pizzas.count)
    return self.pizzas.count
}


//THIS IS WHERE THE CELL IDENTIFIER IS ??
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    //print("IN CELLFORROWAT")

    // tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "cell")

    let cell: PizzaTableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! PizzaTableViewCell

    //cell.backgroundColor = Services.baseColor

    cell.name?.text = pizzas[indexPath.row].name
    cell.pizzaImageView?.image = pizzas[indexPath.row].image
    cell.amount?.text = "(pizzas[indexPath.row].amount)"
    cell.miscellaneousText?.text = pizzas[indexPath.row].description

    print(cell.name?.text! as Any)
    //print(cell.imageView as Any)
    //print("END CELLFORROWAT")

    return cell
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 100.0
}  //END OF

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    performSegue(withIdentifier: "pizza", sender: self.pizzas[indexPath.row] as Pizza)
}  //END OF override func tableView

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "pizza" {
        guard let vc = segue.destination as? PizzaViewController else { return }
        vc.pizza = sender as? Pizza
    }
}  //END OF override preppare func
  

}

Комментарии:

1. Я не совсем уверен, что вы подразумеваете под обратным вызовом fetchInventory, но я попытался поместить фрагмент, который вы указали при вызове fetchInventory, в метод viewDidLoad, а также в конце частной функции fetchInventory, но это не сработало :/ . Если есть что-то еще, что я могу предоставить для дальнейшей отладки этой проблемы, пожалуйста, дайте мне знать, поскольку я нахожусь в тупике

2. @AustinGriffith Это часть кода внутри скобок {} после fecthInventory в начале вашего сообщения, которую вы передаете в качестве параметра.

3. Я пробовал этот метод, и это не решает проблемы :/

4. @AustinGriffith пожалуйста, проверьте обновленное решение. Пожалуйста, обратите внимание, что я издевался над вашим вызовом API.

5. @Alan Scarpa Я убедился, что cell.imageView?.image = pizzas[indexPath.row].image работает и настройка изображения в порядке, но текстовые строки не устанавливаются с помощью cell.name?.text = pizzas[indexPath.row].name и cell.amount?.text = "$(pizzas[indexPath.row].amount)" cell.miscellaneousText?.text = pizzas[indexPath.row].description . Я включил скриншот, чтобы показать его загрузку и ошибку печати nil в консоли для print(cell.name?.text! as Any) . i.imgur.com/PWjj9xf.png @Konstantinos