1
votes

Erreur lors de la définition du texte UILabel dans UITableViewCell personnalisé

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>


10 commentaires

Vous devez avoir une cellule de tableau personnalisée avec plusieurs étiquettes.


Vous définissez cell.imageView? .Image et cell.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 de IBOutlet et définir des valeurs de those , telles que cell.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.png


pourquoi 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 de print (cell.imageView as Any) est facultatif (>) .


@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 dit RootViewController et il ne semble pas qu'une classe lui soit assignée comme les autres le font dans le storyboard.


4 Réponses :


0
votes

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!


4 commentaires

@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' 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



0
votes

 entrez la description de l'image ici

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.


3 commentaires

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 )



1
votes

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

  • Déplacez / copiez votre interface utilisateur de PizzaTableViewCell vers PizzaListTableViewController dans le storyboard à partir de votre "Root View Controller"
  • Assurez-vous d'ajouter un Reuse Identifier dans l'inspecteur d'attributs de la cellule du storyboard
  • supprimer tableView.register (PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza") cela ne vous donnera pas d'erreur cette fois car il sera automatiquement enregistré
  • Assurez-vous que tous les 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.


4 commentaires

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



0
votes

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.

 entrez la description de l'image ici

Assurez-vous que toutes vos prises sont correctement connectées.

 entrez la description de l'image ici

Assurez-vous que votre identifiant de cellule de vue tableau est fourni dans le storyboard.

 entrez la description de l'image ici

Ma sous-classe de contrôleur de vue table

 entrez la description de l'image ici

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.


3 commentaires

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.