Je suis assez nouveau dans la programmation Swift, mais j'ai du mal à définir le texte UILabel dans ma classe UITableView pour des instances UITableViewCell individuelles.
J'ai créé une sous-classe personnalisée de UITableViewCell appelée PizzaTableViewCell
et un classe UITableView personnalisée appelée PizzaListTableViewController
. J'essaie de remplir l'instance UITableView avec les données d'un tableau, qui est rempli à partir d'un appel d'API à mon serveur node.js.
J'ai inclus ma sous-classe UITableView, la classe UITablveViewCell personnalisée, la structure pour les données et un lien vers une capture d'écran du simulateur chargeant ce que j'ai fait. Toute aide est grandement appréciée!
J'ai vérifié que les données sont insérez le tableau sans aucun problème, car je peux imprimer le contenu après l'appel à la méthode fetchInventory
. J'ai pu définir un seul textLabel avec
cell.textLabel? .Text = pizzas [indexPath.row] .name
avec une image dans le tableau avec:
cell.imageView? .image = pizzas [indexPath.row] .image
mais j'ai besoin de 2 autres étiquettes dans chaque cellule que je ne peux pas définir. J'ai vérifié mes identifiants IBOutlets
et Storyboard, et ils correspondent au code.
struct Pizza { let id: String let name: String let description: String let amount: Float //let amount: String 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 } }
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.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza") //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) DispatchQueue.main.async { [weak self] in self?.tableView.reloadData() } } } //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 each item: ", Pizza(data: data)) //printing all inventory successful return Pizza(data: data) } 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 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell: PizzaTableViewCell = tableView.dequeueReusableCell(withIdentifier: "Pizza", for: indexPath) as! PizzaTableViewCell //cell.backgroundColor = Services.baseColor //cell.pizzaImageView?.image = pizzas[indexPath.row].image //THESE WORK BUT ARE A STATIC WAY OF SETTING THE CELLS //CAN ONLY SET THE SELL WITH A SINGLE TEXT LABEL FROM THE DATA ARRAY cell.imageView?.image = pizzas[indexPath.row].image cell.textLabel?.text = pizzas[indexPath.row].name //cell.textLabel?.text = pizzas[indexPath.row].description //cell.textLabel?.text = "$\(pizzas[indexPath.row].amount)" // 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) 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: "pizzaSegue", sender: self.pizzas[indexPath.row] as Pizza) } //END OF override func tableView override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "pizzaSegue" { guard let vc = segue.destination as? PizzaViewController else { return } vc.pizza = sender as? Pizza } } //END OF override preppare func }
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.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza") //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) DispatchQueue.main.async { [weak self] in self?.tableView.reloadData() } } } //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 each item: ", Pizza(data: data)) //printing all inventory successful return Pizza(data: data) } 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 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell: PizzaTableViewCell = tableView.dequeueReusableCell(withIdentifier: "Pizza", for: indexPath) as! PizzaTableViewCell //cell.backgroundColor = Services.baseColor //cell.pizzaImageView?.image = pizzas[indexPath.row].image //THESE WORK BUT ARE A STATIC WAY OF SETTING THE CELLS //CAN ONLY SET THE SELL WITH A SINGLE TEXT LABEL FROM THE DATA ARRAY cell.imageView?.image = pizzas[indexPath.row].image cell.textLabel?.text = pizzas[indexPath.row].name //cell.textLabel?.text = pizzas[indexPath.row].description //cell.textLabel?.text = "$\(pizzas[indexPath.row].amount)" // 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) 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: "pizzaSegue", sender: self.pizzas[indexPath.row] as Pizza) } //END OF override func tableView override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "pizzaSegue" { guard let vc = segue.destination as? PizzaViewController else { return } vc.pizza = sender as? Pizza } } //END OF override preppare func }
Comme indiqué ci-dessus, j'ai pu imprimer le contenu du tableau de données avec des noms de bière, des images, des descriptions, etc. J'ai essayé d'imprimer sur la console
print (cell. name? .text)
après le réglage
cell.name?.text = pizzas [indexPath.row] .name
p >
mais il affiche nil
et c'est un problème. Cela fait environ 2 semaines que je suis bloqué avec ça!
Capture d'écran IBOutlets:
p>
4 Réponses :
Essayez ceci
cell.name?.text = ... cell.amount?.text = ... cell.miscellaneousText?.text = ... cell.pizzaImageView?.image = ...
Si cela ne fonctionne toujours pas, assurez-vous que votre cellule et vos points de vente ne sont pas nuls lors de la définition de sa valeur. J'espère que ça aide!
@DuyNguyen J'ai déjà essayé cela comme indiqué dans mon code ci-dessus. Je l'ai commenté en ce moment car lors de l'utilisation de cette méthode, ma vue de tableau charge des cellules vides sans étiquettes ni image. La seule façon dont j'ai pu obtenir un seul textLabel à charger et les images à charger dans chaque cellule est avec cette notation: cell.imageView? .Image = pizzas [indexPath.row] .image cell.textLabel ?. text = pizzas [indexPath.row] .name
. Si j'essaie d'ajouter une autre étiquette telle que cell.textLabel? .Text = pizzas [indexPath.row] .description
La deuxième affectation remplace la première et une seule charge.
@AustinGriffith essayez de commenter cette ligne de code tableView.register (PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza")
@DuyNguyen Si je supprime cette ligne de code, je suis confronté à cette erreur dans la console. *** Terminer l'application en raison d'une exception non interceptée 'NSInternalInconsistencyException', raison: 'impossible de retirer une cellule avec l'identifiant PizzaCell - doit enregistrer une pointe ou une classe pour l'identifiant ou connecter une cellule prototype dans un storyboard' code > Je sais que j'ai également mis l'identifiant de cellule dans le générateur d'interface, je ne sais donc pas pourquoi j'obtiens cela.
@AustinGriffith essayez de changer Pizza
en PizzaCell
dans cette ligne laissez cell: PizzaTableViewCell = tableView.dequeueReusableCell (withIdentifier: "Pizza", pour: indexPath) comme! PizzaTableViewCell
Je suppose que PizzaCell
est défini comme identifiant de cellule dans l'IB, si vous n'enregistrez pas de classe pour tableview, alors il recherche la cellule d'IB qui a l'identifiant de cellule exact
Il se passe quelque chose de vraiment étrange avec votre configuration. Si vous essayez de nommer les IBOutlets avec le même nom que la propriété par défaut UITableViewCell, une erreur sera générée. Le fait que vous ayez pu définir ces noms et construire avec succès est étrange.
Dans la capture d'écran ci-dessus, vous pouvez voir ce qui se passe lorsque j'ai tenté de le faire.
Souhaitez-vous ajouter une capture d'écran de l'inspecteur d'identité pour votre uitableviewcell personnalisée?
J'ai renommé les points de vente pour qu'ils soient quelque chose de différent des étiquettes et autres dans Interface Builder, je ne pense pas qu'ils aient jamais été les mêmes!
Voici un lien vers la capture d'écran d'Identity Inspector pour ma tableviewcell personnalisée. Dans ma classe de cellule personnalisée, j'ai renommé les IBOutlets comme suit: @IBOutlet faible var cellName: UILabel! @IBOutlet faible var cellAmount: UILabel! @IBOutlet faible var cellDescription: UILabel! @IBOutlet faible var cellImage: UIImageView!
Ce sont les noms que vous verrez dans le générateur d'interface -> Inspecteur d'identité pour la cellule ( i.imgur.com/Da34abs.png )
Je pense que j'ai trouvé votre problème, laissez-moi vous expliquer
Ce que vous faites ici, c'est que vous avez un UITableViewCell
personnalisé défini dans le Storyboard
dans un contrôleur nommé " Root View Controller "qui n'est pas votre PizzaListTableViewController
pour le dire simplement
Et comme vous l'avez dit, vous n'avez absolument aucun problème concernant les IBOutlets
Maintenant, quand vous dites
tableView.register(UINib(nibName: "PizzaTableViewCell", bundle: Bundle.main), forCellReuseIdentifier: "PizzaCell")
Dans votre PizzaListTableViewController
, vous ne le liez pas avec l'interface utilisateur de la cellule plutôt qu'avec le code (c'est utilisé uniquement lorsqu'il n'y a pas de xib de la cellule)
Maintenant ce que vous pouvez faire pour résoudre ce problème
Solution n ° 1
PizzaTableViewCell
vers PizzaListTableViewController
dans le storyboard à partir de votre "Root View Controller" Reuse Identifier
dans l'inspecteur d'attributs de la cellule du storyboard tableView.register (PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza")
cela ne vous donnera pas d'erreur cette fois car il sera automatiquement enregistré IBOutlets
sont connectés Solution n ° 2
créer un Nib
( xib
) distinct de la cellule
et maintenant vous devez enregistrer la cellule ici comme
tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza")
J'espère que cela vous aidera.
J'ai essayé votre deuxième solution et l'application se bloque lorsque la tableView est chargée. J'obtiens cette erreur: Terminer l'application en raison d'une exception non interceptée 'NSInternalInconsistencyException', raison: 'Impossible de charger NIB dans le paquet:' NSBundle <... DrinkupClient.app> (chargé) 'avec le nom' DrinkTableViewCell '' < / code>. J'ai changé le nom de l'identifiant en "cellule" et des mises à jour dans le constructeur d'interface et dans le code.
@AustinGriffith Pour le second, vous devez créer un fichier PizzaTableViewCell.xib
.
J'essaie également de comprendre la première partie de votre solution n ° 1. Je ne comprends pas ce que vous entendez par "Déplacer / Copier votre interface utilisateur de PizzaTableViewCell vers PizzaListTableViewController dans le storyboard à partir de votre" Root View Controller "". J'ai retiré la ligne tableView.register (...)
mais cela plante l'application Terminating app en raison d'une exception non interceptée 'NSInternalInconsistencyException', raison: 'impossible de retirer une cellule avec identificateur de cellule - doit enregistrer une nib ou une classe pour l'identifiant ou connecter une cellule prototype dans un storyboard '
Oui, vous devriez obtenir l'erreur si vous n'avez pas copié votre Copiez votre cellule d'interface utilisateur dans le storyboard
dans votre PizzaList TableViewController
Assurez-vous que votre classe Table View Controller est définie dans le storyboard.
Assurez-vous que votre classe Table View Cell est définie dans le storyboard.
Assurez-vous que toutes vos prises sont correctement connectées.
Assurez-vous que votre identifiant de cellule de vue tableau est fourni dans le storyboard.
Ma sous-classe de contrôleur de vue table
Sous-classe de cellules My Table View
cell.imageView? .image et cell.textLabel? .text sont des propriétés facultatives de la vue de tableau elle-même. Ce ne sont pas les propriétés de la cellule personnalisée que vous avez conçue.
Vous utilisez tableView.register (PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza") lorsque vous avez conçu une cellule de vue tableau dans XIB. Mais comme vous avez conçu la cellule dans le storyboard lui-même, vous devez définir l'identifiant de réutilisation de cellule et la classe de cellule dans le storyboard.
J'espère que cela vous aidera.
Merci pour les photos détaillées. En fait, je suis passé par ces étapes exactes une poignée de fois. Je sais que cell.imageView? .Image et cell.textLabel? .Text sont des propriétés facultatives de la vue tableau, mais même en essayant de définir les étiquettes et les images avec ma classe personnalisée, la vue tableau se charge simplement comme vide, rien n'apparaît sauf l'application ne plante pas, donc je ne sais toujours pas pourquoi je ne peux pas les définir et voir les résultats dans le simulateur. Les valeurs de print (cell.cellName? .Text as Any)
continuent de s'imprimer comme nulles dans la console pour tous les cell.cellAmount, cell.cellDescription, etc.
Puis-je avoir une image de votre sous-classe UITableViewCell? Si cellName est une prise dans votre classe de cellules, elle ne doit pas être facultative. Veuillez supprimer le code tableView.register () de votre contrôleur de vue.
@ArijitiSarkar J'ai lié ici à un git gist car j'ai un peu changé le code. ( gist.github.com/Austin-Griffith/… ) À partir de maintenant dans mon code si je supprime la ligne tableView.register () dans la méthode viewDidLoad, l'application se bloque une fois qu'elle a pu charger la tableView. Dans ma classe personnalisée UITableViewCell, je n'ai pas la cellule IBOutlets définie comme option. La façon dont je le fais en ce moment utilise le style de cellule: sous-titre et définissant les cellules par défaut texLabel, detailTextLabel et UIImage avec les données de mon tableau.
Vous devez avoir une cellule de tableau personnalisée avec plusieurs étiquettes.
Vous définissez
cell.imageView? .Image
etcell.textLabel? .Text
, mais ce sont des éléments par défaut d'une cellule de vue tableau par défaut. Si vous utilisez une cellule personnalisée, vous devez donner à ces éléments des noms différents deIBOutlet
et définir des valeurs de those , telles quecell.myTextLabel? .text
@TejaNandamuri J'ai en effet une cellule de tableau personnalisée et elle a 3 étiquettes de texte et une image d'interface utilisateur. La classe de cellule personnalisée s'appelle PizzaTableViewCell et j'ai défini 4 IBOutlets et les ai connectés au storyboard.
swift @IBOutlet faible nom de la variable: UILabel! @IBOutlet faible var pizzaImageView: UIImageView! @IBOutlet faible montant var: UILabel! @IBOutlet faible var diversTexte: UILabel!
@DonMag J'ai en effet essayé de définir les étiquettes avec cette notation. Si vous regardez dans le code ci-dessus, je l'ai commenté.
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
. cell.name fait référence à l'IBOutlet que j'ai défini dans la classe de cellule vue table personnalisée, voici un lien vers ce code. i.imgur.com/EDG9Ogt.pngpourquoi commentez-vous cell.miscellaneous Text? .text? quelle est l'erreur?
@TejaNandamuri Lorsque j'essaie de définir le libellé de texte de cette façon, la tableView ne charge que les images dans chacune des cellules. Aucune des étiquettes ne se charge dans les cellules. J'imprime également le contenu de la cellule.
print (cell.miscellaneousText? .text)
et quand je fais chacune des étiquettes sont nulles mais le contenu deprint (cell.imageView as Any)
estfacultatif (>)
.@TejaNandamuri Je suis vraiment perdu car je ne comprends pas pourquoi les données seront correctement définies sur cell.name, cell.amount et cell.miscellaneous lorsque j'ai prouvé que les données proviennent correctement de l'appel d'API à mon tableau de données.
essayez de supprimer tableView.register (PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza")
veuillez consulter ceci pour un exemple: ralfebert.de/ios-examples/uikit / uitableviewcontroller /…
Désolé, je ne peux pas m'empêcher de me demander si vous avez réellement attribué
PizzaListTableViewController
comme sous-classe pour votre contrôleur tableview dans le storyboard. Pour le moment, il ditRootViewController
et il ne semble pas qu'une classe lui soit assignée comme les autres le font dans le storyboard.