#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 тоже внутри этого блока основного потока.
И, пожалуйста, примите во внимание совет Алана по двойному вызову.
-
Пожалуйста, полностью удалите регистр из TableView / cellForRow.
// tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "cell")
-
Вместо:
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