在自定义UITableViewCell

时间:2019-04-09 17:23:33

标签: ios arrays swift xcode uitableview

我对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和情节提要标识符,它们与代码匹配。

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,这是一个问题。我已经坚持了大约两个星期!

IBOutlets屏幕截图:

4 个答案:

答案 0 :(得分:1)

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

您在这里做的是在UITableViewCell的一个名为“ Root View Controller”的控制器中定义了一个自定义Storyboard,这不是您的PizzaListTableViewController的简单表达

正如您所说,您对IBOutlets

绝对没有问题

现在当你说

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

在您的PizzaListTableViewController中,您并未将其与单元格的UI链接,而仅将其与代码链接(仅当单元格没有xib时才使用)

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

解决方案1 ​​

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

解决方案2

为该单元格创建一个单独的Nibxib) 现在您必须像这样在这里注册单元格

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

希望这会有所帮助。

答案 1 :(得分:0)

尝试一下

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

如果仍然不起作用,请在设置其值时确保单元格和插座不为空。希望对您有帮助!

答案 2 :(得分:0)

enter image description here

在您的设置中确实发生了一些奇怪的事情。 如果您尝试使用与UITableViewCell默认属性相同的名称来命名IBOutlet,则会引发错误。您能够设置这些名称并成功构建的事实很奇怪。

从上面的屏幕截图中,您可以看到尝试执行此操作时发生的情况。

答案 3 :(得分:0)

确保在情节提要中设置了Table View Controller类。

确保在情节提要中设置了Table View Cell类。

enter image description here

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

enter image description here

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

enter image description here

我的表视图控制器子类

enter image description here

我的表格视图单元格子类

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

在XIB中设计表格视图单元格时,请使用 tableView.register(PizzaTableViewCell.self,forCellReuseIdentifier:“ Pizza”)。但是,当您在情节提要板上设计了单元格时,应该在情节提要中设置单元格重用标识符和单元格类。

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