如何使用自定义标题将单元格排列成节?

4
我该如何通过自定义标题将单元格按部分排列?
我已经编写了代码,通过品牌(将品牌放在CartHeaderCell中)来排列CartVC中的单元格。当数据从HomeVC传递到CartVC时,我无法让我的代码根据品牌将单元格排列成部分(当前代码将数据从HomeVC传递到CartVC,而不将单元格排列成部分)。
更新:
现在,CartViewController Extension中的代码将单元格分组并通过品牌将项目传递到单元格中,但是会将所有单元格随机分配到不同的部分或为单元格中的品牌创建新的部分,并且/或者在单击CartBtn时会使模拟器崩溃,或在多个部分中显示相同的项目/单元格。

image

extension HomeController: UITableViewDelegate, UITableViewDataSource {

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return itemSetup.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "HomeCell") as? HomeCell else { return UITableViewCell() }

        let item = itemSetup[indexPath.row]
        cell.configure(withItems: item)

        // passes data to the Cart Cells in the CartVC when ATC Btn is pressed in each HomeCell
        cell.addActionHandler = { (option: Int) in
            print("Option selected = \(option)")
            Tray.currentCart.cartItems.append(item)
            item.selectedOption = option
        }

        return cell
    }
}

import UIKit

class CartViewController: UIViewController {

    var items: Items!

    // arranges cells into sections
    var tray: [Tray] = []
    var sortedBrandsByName: [String] = []
    var sections: [[Tray]] = [[]]

    @IBOutlet weak var cartTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // arranges cells into sections
        let brandNames = tray.map { $0. titleBrandName }
        let uniqueBrandNames = Array(Set(brandNames))           
        let sortedBrandNames = uniqueBrandNames.sorted()
        let sections: [[Tray]] = sortedBrandNames.map { firstBrandNames in
            return tray
                .filter { $0. titleBrandName == firstBrandNames } 
                .sorted { $0.cart.brand < $1.cart.brand } // sort them
        }

        // Do any additional setup after loading the view.
        cartTableView.dataSource = self
        cartTableView.delegate = self

    }
}

extension CartViewController: UITableViewDataSource, UITableViewDelegate {

    func numberOfSections(in tableView: UITableView) -> Int {
        return Tray.currentCart.cartItems.count
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        //allows data passed from the HomeVC populate the CartCells
        return Tray.currentCart.cartItems[section].count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CartCell", for: indexPath) as! CartCell

        // **Active code** that allows data passed from the HomeVC populate the CartCells
        let cart = Tray.currentCart.cartItems[indexPath.row]
        cell.configure(withItems: cart)

        return cell
    }

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let cartHeader = tableView.dequeueReusableCell(withIdentifier: "CartHeaderCell") as! CartHeaderCell
        cartHeader.storeName.text = Tray.currentCart.cartItems[section].brand
        return cartHeader
    }

    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 45
     } 
}

class CartHeaderCell: UITableViewCell {

    @IBOutlet weak var brandName: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }
}

class CartCell: UITableViewCell {

    @IBOutlet weak var lblMealName: UILabel!
    @IBOutlet weak var imageUrl: UIImageView!
    @IBOutlet weak var lblSubTotal: UILabel!
    @IBOutlet weak var lblQty: UILabel!

    override func awakeFromNib() {
         super.awakeFromNib()
        // Initialization code
    }

    // allows the data to be passed into the cart cells

    func configure(withItems items: Items) {
        imageUrl.sd_setImage(with: URL(string: items.imageUrl))
        lblQty.text = "\(items.count)"
        let formatter = NumberFormatter()
        formatter.maximumFractionDigits = 2
        formatter.numberStyle = .decimal
        if items.selectedOption == 1 {
            lblSubTotal.text = "$\(formatter.string(for: items.price1 * Float(items.count))!)"
            lblMealName.text = "\(items.name)\(items.weight1)"
        } else if items.selectedOption == 2 {
            lblSubTotal.text = "$\(formatter.string(for: items.price2 * Float(items.count))!)"
            lblMealName.text = "\(items.name)\(items.weight2)"
        } else if items.selectedOption == 3 {
            lblSubTotal.text = "$\(formatter.string(for: items.price3 * Float(items.count))!)"
            lblMealName.text = "\(items.name)\(items.weight3)"
        }
    } 
}

// allows the code that is passed to the CartVC when an item is passed from the HomeVC to the CartVC
class Tray {
    static let currentCart = Tray()
    var cartItems = [Items]()
    var cart: Items!
    var sectionTitle: String!
}

extension Tray {
    var titleBrandName: String {
        return String(self.cart.brand[self.cart.brand.startIndex]).uppercased()
    }
}

一个部分的标题不应该被实现为一行。更好的实现方式是 -
  1. 使用品牌名称作为键,相应值在数组中的字典代替数据源的数组。
  2. numberOfSections 中提供字典的键计数。
  3. viewForHeaderInSection 中配置部分标题。
- brainforked
我刚刚在我的CartVC和Tray类中添加了一些代码(标记为“测试代码”),我认为这是实现你的解决方案的第一步。很抱歉回复晚了,我的整个社区的WiFi都出问题了,我终于能去当地咖啡店做这件事了。 - Evelyn
我刚在numberOfSections中测试了这个,我放置了return Tray.currentCart.cartItems.count,并且在numberOfRowsInSection中我放置了return Tray.currentCart.cartItems[section].count。虽然当数据传递时它显示了标题,并将单元格分成了几个部分,但它并不准确,实际上它完全混乱了所有项目。而且无论我尝试什么,品牌都无法显示在标题中。 - Evelyn
你能发布表格数据源吗? - brainforked
我认为 Tray 的模型是错误的,因为从图片中看出有多个品牌,而你使用的 titleBrandName 是一个单一属性而不是数组。除此之外,请发表 Items 的模型类,这样我就可以更仔细地查看如何排列这些物品了。 - Shubham Ojha
刚刚更新了我的代码 @ShubhamOjha - Evelyn
1个回答

2

问题在于您实际上没有保存您所做的排序。即使您保存了,您也在UITableViewDataSource方法中查看代码的不同部分。

首要问题:您实际上没有保存您的数据。 确保创建CartViewController的函数在viewDidLoad:之前传递tray

在这个部分中:

import UIKit

class CartViewController: UIViewController {

    var items: Items!

    // arranges cells into sections
    var tray: [Tray] = []
    var sortedBrandsByName: [String] = []
    var sections: [[Tray]] = [[]]

    @IBOutlet weak var cartTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // arranges cells into sections
        let brandNames = tray.map { $0. titleBrandName }
        let uniqueBrandNames = Array(Set(brandNames))           
        let sortedBrandNames = uniqueBrandNames.sorted()
        let sections: [[Tray]] = sortedBrandNames.map { firstBrandNames in
            return tray
                .filter { $0. titleBrandName == firstBrandNames } 
                .sorted { $0.cart.brand < $1.cart.brand } // sort them
        }

        // Do any additional setup after loading the view.
        cartTableView.dataSource = self
        cartTableView.delegate = self

    }
}

您的viewDidLoad方法会创建sortedBrandNamessections的本地版本。请移除这些let关键字,尝试以下代码:

override func viewDidLoad() {
    super.viewDidLoad()

    // arranges cells into sections
    let brandNames = tray.map { $0. titleBrandName }
    let uniqueBrandNames = Array(Set(brandNames)) 

    // MY CHANGES ARE BELOW (remove let)      
    sortedBrandNames = uniqueBrandNames.sorted()
    sections: [[Tray]] = sortedBrandNames.map { firstBrandNames in
        return tray
            .filter { $0. titleBrandName == firstBrandNames } 
            .sorted { $0.cart.brand < $1.cart.brand } // sort them
    }

    // Do any additional setup after loading the view.
    cartTableView.dataSource = self
    cartTableView.delegate = self

}

总体反馈:你应该使用这些来填充表视图,而不是静态的Tray.currentTray。在任何代码库中从任何地方读取/写入值的数据保持都是非常糟糕的。

其次...你正在UITableViewDataSource方法中读取不同的数据集...

假设我们现在成功保存了排序后的数据... 当你执行Tray.currentCart时,实际上你并没有查看刚刚排序过的数据(那将是self.sections),在任何情况下,你都没有改变Tray.currentCart中的数据。(再次强调,不要制造像这样的静态内容或单例,这样很容易引入错误)

   func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        //allows data passed from the HomeVC populate the CartCells
        return Tray.currentCart.cartItems[section].count
    }

不要使用... 尝试这样做:

extension CartViewController: UITableViewDataSource, UITableViewDelegate {

    func numberOfSections(in tableView: UITableView) -> Int {
        return self.sections.count
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.sections[section].count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CartCell", for: indexPath) as! CartCell

        let cart = self.sections[indexPath.section][indexPath.row]
        cell.configure(withItems: cart)

        return cell
    }

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let cartHeader = tableView.dequeueReusableCell(withIdentifier: "CartHeaderCell") as! CartHeaderCell
        cartHeader.storeName.text = self.sections[section].brand
        return cartHeader
    }

    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 45
     } 
}

这样,你就可以在 viewDidLoad 中查看刚刚排序的数据了。(注意,我假设你的排序逻辑是正确的)


好的,我刚刚运行了代码,出现了错误。在删除 Let 后,在 viewDidLoad 中会出错。在cell.configure(withItems: cart)cellForRowAt中,出现了错误**Cannot convert value of type 'Tray' to expected argument type 'ProductList',而在cartHeader.storeName.text = self.sections[section].brandviewForHeaderInSection中,出现了错误Value of type '[Tray]' has no member 'brand'**。我会尽力解决这些错误,看看能否使其正常工作,但现在它还不能正常工作。 - Evelyn
你说得没错,我正在努力重构我的代码,将currentCart从托盘类中移除。但是需要一些时间来重写我的代码,因为它的属性被用于从HomeVC(不在这里的代码)的闭包中传递数据到CartVC。 - Evelyn
1
如果你想向上传递数据,可以使用类似委托的方式。在HomeVC初始化CartVC,设置cartVC.delegate = self,然后就可以这样传递数据了。一定要谷歌一下,因为你需要声明代理为“弱引用”,以避免保留循环和其他最佳实践。 - gadu
尝试过了,但不起作用,因为我有一个闭包,它将数据从HomeVC传递到CartVC。 - Evelyn
运行了一堆测试来让这段代码工作,现在它可以正常运行了。很抱歉花了这么长时间才解决问题,最近网络不太好,而且上周工作量也非常大。 - Evelyn
很高兴你搞定了!是的,因为你排序的数据与“currentCart”中的数据不完全匹配,所以需要进行一些更改,但是想法肯定是正确的。 - gadu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接