【问题标题】:Error setting UILabel text in custom UITableViewCell在自定义 UITableViewCell 中设置 UILabel 文本时出错
【发布时间】:2019-04-09 17:23:32
【问题描述】:

我对 Swift 编程很陌生,但是在我的 UITableView 类中为单个 UITableViewCell 实例设置 UILabel 文本时遇到了麻烦。

我创建了一个名为PizzaTableViewCell 的自定义UITableViewCell 子类和一个名为PizzaListTableViewController 的自定义UITableView 类。我正在尝试使用数组中的数据填充 UITableView 实例,该数组是从对我的 node.js 服务器的 API 调用填充的。

我已经包含了我的 UITableView 子类、自定义 UITablveViewCell 类、数据结构,以及一个指向加载我所做的模拟器的屏幕截图的链接。非常感谢任何帮助!

我已经验证数据被放入数组中没有问题,因为我可以在调用fetchInventory 方法后打印内容。我已经能够使用

设置单个 textLabel

cell.textLabel?.text = pizzas[indexPath.row].name

连同数组中的图像:

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

但我在每个单元格中还需要 2 个无法设置的标签。我检查了我的 IBOutlets 和 Storyboard 标识符,它们与代码匹配。

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

}
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
    }

}

如上所述,我已经能够打印包含啤酒名称、图片、描述等的数据数组的内容。我尝试打印到控制台

print(cell.name?.text)

设置后

cell.name?.text = pizzas[indexPath.row].name

但它会打印nil,这是一个问题。我已经坚持了大约 2 周!

IBOutlets 截图:

【问题讨论】:

  • 您需要有一个带有多个标签的自定义表格单元格。
  • 您正在设置cell.imageView?.imagecell.textLabel?.text,但它们是默认表格视图单元格的默认元素。如果您使用的是自定义设计的单元格,则需要为这些元素指定不同的 IBOutlet 名称并设置 那些 值,例如 cell.myTextLabel?.text
  • @TejaNandamuri 我确实有一个自定义表格单元格,它有 3 个文本标签和一个 UI 图像。自定义单元格类称为 PizzaTableViewCell,我设置了 4 个 IBOutlets 并将它们连接到情节提要。 swift @IBOutlet weak var name: UILabel! @IBOutlet weak var pizzaImageView: UIImageView! @IBOutlet weak var amount: UILabel! @IBOutlet weak var miscellaneousText: UILabel!
  • @DonMag 我确实尝试过使用该符号设置标签。如果您查看上面的代码,我已经将这种方式注释掉了。 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 指的是我在自定义表格视图单元格类中设置的 IBOutlet,这是该代码的链接。 i.imgur.com/EDG9Ogt.png
  • 抱歉,不禁想知道您是否实际上已将PizzaListTableViewController 指定为情节提要中tableview 控制器的子类。现在它说RootViewController,它看起来不像故事板中的其他人那样分配了一个类。

标签: ios arrays swift xcode uitableview


【解决方案1】:

我想我找到了你的问题,让我解释一下

您在这里所做的是您在名为“根视图控制器”的控制器中的 Storyboard 中定义了一个自定义 UITableViewCell,简而言之,这不是您的 PizzaListTableViewController

正如你所说,IBOutlets 绝对没有问题

现在当你说

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

在您的PizzaListTableViewController 中,您没有将其与单元格的 UI 链接,而只是将代码链接到代码(仅在单元格没有 xib 时使用)

现在你可以做些什么来解决这个问题

解决方案 #1

  • 从“根视图控制器”在情节提要中将 PizzaTableViewCell 的 UI 移动/复制到 PizzaListTableViewController
  • 确保在情节提要中单元格的属性检查器中添加Reuse Identifier
  • 删除tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza")这一次不会给你一个错误,因为它会自动注册
  • 确保所有IBOutlets 都已连接

解决方案 #2

创建一个单独的Nib (xib) 的单元格 现在你必须在这里注册单元格

tableView.register(UINib(nibName: "PizzaTableViewCell", bundle: Bundle.main), forCellReuseIdentifier: "PizzaCell")

希望这会有所帮助。

【讨论】:

  • 我已经尝试了您的第二种解决方案,但当 tableView 加载时应用程序崩溃了。我收到此错误:Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle <...DrinkupClient.app> (loaded)' with name 'DrinkTableViewCell''。我已将标识符的名称更改为“单元格”,并在界面生成器和代码中进行了更新。
  • @AustinGriffith 对于第二个,您必须创建一个 PizzaTableViewCell.xib 文件。
  • 我也在尝试理解您的解决方案#1 的第一部分。我不明白您所说的“将 PizzaTableViewCell 的 UI 从您的“根视图控制器”移动/复制到情节提要中的 PizzaListTableViewController 是什么意思。我已经取消了tableView.register(...) 这条线,但这会使应用程序崩溃Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier cell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
  • 是的,如果您没有将 Copy your UI cell in the story board 复制到您的 PizzaListTableViewController,您应该会收到错误消息
【解决方案2】:

试试这个

cell.name?.text = ...
cell.amount?.text = ...
cell.miscellaneousText?.text = ...
cell.pizzaImageView?.image = ...

如果仍然无法正常工作,请确保您的单元格和插座在设置其值时不为空。希望对您有所帮助!

【讨论】:

  • @DuyNguyen 我已经尝试过,如上面代码中所述。我现在把它注释掉了,因为当使用这种方法时,我的表格视图会加载没有标签或图像的空白单元格。我能够加载单个 textLabel 并在每个单元格中加载图像的唯一方法是使用以下符号:cell.imageView?.image = pizzas[indexPath.row].image cell.textLabel?.text = pizzas[indexPath.row].name。如果我尝试添加另一个标签,例如cell.textLabel?.text = pizzas[indexPath.row].description,第二个分配会覆盖第一个也是唯一一个加载。
  • @AustinGriffith 尝试注释掉这行代码tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza")
  • @DuyNguyen 如果我删除那行代码,我会在控制台中遇到此错误。 *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier PizzaCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard' 我知道我也将单元标识符放入了界面生成器中,所以我很不确定为什么会得到这个。
  • @AustinGriffith 尝试在这一行中将Pizza 更改为PizzaCell let cell: PizzaTableViewCell = tableView.dequeueReusableCell(withIdentifier: "Pizza", for: indexPath) as! PizzaTableViewCell 我假设PizzaCell 被设置为IB 中的单元格标识符,如果您没有为tableview 注册类,那么它从 IB 中查找具有确切单元标识符的单元
【解决方案3】:

您的设置确实有些奇怪。 如果您尝试将 IBOutlets 命名为与 UITableViewCell 默认属性相同的名称,则会引发错误。您能够设置这些名称并成功构建的事实很奇怪。

从上面的屏幕截图中,您可以看到当我尝试这样做时会发生什么。

【讨论】:

  • 您会为您的自定义 tableviewcell 添加身份检查器的屏幕截图吗?
  • 我已将网点重命名为与 Interface builder 中的标签等不同的东西,我认为它们不一样!
  • 这是我的自定义 tableviewcell 的 Identity Inspector 屏幕截图的链接。在我的自定义单元格类中,我已将 IBOutlets 重命名为以下内​​容:@IBOutlet weak var cellName: UILabel! @IBOutlet weak var cellAmount: UILabel! @IBOutlet weak var cellDescription: UILabel! @IBOutlet weak var cellImage: UIImageView! 这些是您将在界面构建器中看到的名称 -> 单元格的身份检查器 (i.imgur.com/Da34abs.png)
【解决方案4】:

确保您的 Table View Controller 类已在情节提要中设置。

确保您的 Table View Cell 类已在情节提要中设置。

确保所有插座均已正确连接。

确保情节提要中提供了您的表格视图单元格标识符。

我的表视图控制器子类

我的表格视图单元格子类

cell.imageView?.imagecell.textLabel?.text 是表格视图本身的可选属性。它们不是您设计的自定义单元格的属性。

当您在 XIB 中设计了表格视图单元格时,您使用 tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza")。但是,由于您已经在情节提要本身中设计了单元,您应该在情节提要中设置单元重用标识符和单元类。

我希望这会对你有所帮助。

【讨论】:

  • 感谢您提供详细的图片。我实际上已经经历了几次这些确切的步骤。我知道 cell.imageView?.image 和 cell.textLabel?.text 是表格视图的可选属性,但即使尝试使用我的自定义类设置标签和图像,表格视图只是加载为空白,除了应用程序之外什么都没有出现不会崩溃,所以我仍然很困惑为什么我不能设置它们并在模拟器中查看结果。对于所有 cell.cellAmount、cell.cellDescription 等,print(cell.cellName?.text as Any) 的值仍会在控制台中继续打印为 nil
  • 可以给我一张你的 UITableViewCell 子类的图片吗?如果 cellName 是你的 cell 类中的一个出口,它不应该是可选的。请从您的视图控制器中删除 tableView.register() 代码。
  • @ArijitiSarkar 我已经在这里链接到一个 git gist,因为我已经稍微修改了代码。 (gist.github.com/Austin-Griffith/…) 现在在我的代码中,如果我删除 viewDidLoad 方法中的 tableView.register() 行,应用程序在加载 tableView 时会崩溃。在我的 UITableViewCell 自定义类中,我没有将单元格 IBOutlets 设置为可选项。我现在的工作方式是使用单元格样式:副标题并将单元格设置为默认 texLabel、detailTextLabel 和 UIImage 以及我数组中的数据。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-05
  • 2017-06-04
  • 2023-04-03
相关资源
最近更新 更多