How to fix the separation collections of cells in my sections in my tableview?

半城伤御伤魂 提交于 2020-01-06 06:04:13

问题


So I've been told that my data model for separating my cells into sections is bad.

I have 3 separate collections in my Tableview cartItems, groupedItems, and brandTitle in the CartVC. and I've been told this:

"You need to start over with a single collection representing nothing but sections data (where each piece of section data will hold the corresponding row data), so you can mutate the model without going insane"

and

"...recommended to avoid multiple Arrays for the datasource of a table view and using Dictionary to feed the table view is not a good idea. You should create a Model type."

I just don't know really how to do so, since the collections I've been using to separate the cells into sections took me weeks to generate and figure out, so that I could successfully populate the cells in their appropriate sections in the CartVC

import UIKit

class CartViewController: UIViewController {

    var selectedProduct: ItemList!       // allows data to be passed into the CartVC

    // allows data to be sepearted into sections
    var cartItems: [CartItem] = []
    var groupedItems: [String: [CartItem]] = [:]
    var brandTitle: [String] = []

    @IBOutlet weak var cartTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.

        groupedItems = Dictionary(grouping: cartItems, by: {$0.itemList.brandName})
        brandTitle = groupedItems.map{$0.key}.sorted()
    }
}

extension CartViewController: UITableViewDelegate, UITableViewDataSource{

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

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        let brand = brandTitle[section]
        return groupedItems[brand]!.count
    }

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

        let brand = brandTitle[indexPath.section]
        let itemsToDisplay = groupedItems[brand]![indexPath.row]
        cartCell.configure(withCartItems: itemsToDisplay.productList)

        return cartCell
    }

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let cartHeader = tableView.dequeueReusableCell(withIdentifier: "CartHeader") as! CartHeader

        let headerTitle = brandTitle[section]
        cartHeader.brandName.text = "Brand: \(headerTitle)"

        return cartHeader
    }

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

    func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
        let cartFooter = tableView.dequeueReusableCell(withIdentifier: "CartFooter") as! CartFooter

        let brand = brandTitle[section]
        let subtotal = groupedItems[brand]?.map { $0.getCartTotal() }.reduce(0, +) ?? 0
        cartFooter.cartTotal.text = String(subtotal)

        return cartFooter
    }

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

import Foundation

class CartItem {
    var itemList: ItemList!

    init(itemList: ItemList) {

        self.itemList = itemList
    }

    func getCartTotal() -> Float {
        var subtotal: Float = 0
        if itemList.selectedOption == 1 {
            subtotal = subtotal + (Float(itemList.price1) * Float(itemList.count))
        } else if itemList.selectedOption == 2 {
            subtotal = subtotal + (Float(itemList.price2) * Float(itemList.count))
        } else if itemList.selectedOption == 3 {
           subtotal = subtotal + (Float(itemList.price3) * Float(itemList.count))
        }  else {
            fatalError("The impossible happend")
        }
        return subtotal
    }
}

class ItemList {
    var id: String
    var name: String
    var brand: String
    var price1: Float
    var price2: Float
    var price3: Float
    var weight1: String
    var weight2: String
    var weight3: String
    var imageUrl: String
    var selectedOption: Int
    var count: Int

    init(id: String,
         name: String,
         brand: String,
         price1: Float,
         price2: Float,
         price3: Float,
         weight1: String,
         weight2: String,
         weight3: String,
         imageUrl: String,
         selectedOption: Int,
         count: Int) {

        self.id = id
        self.name = name
        self.brand = brand
        self.price1 = price1
        self.price2 = price2
        self.price3 = price3
        self.weight1 = weight1
        self.weight2 = weight2
        self.weight3 = weight3
        self.imageUrl = imageUrl
        self.selectedOption = selectedOption
        self.count = count
    }

    convenience init(dictionary: [String : Any]) {
        let id = dictionary["id"] as? String ?? ""
        let name = dictionary["name"] as? String ?? ""
        let brand = dictionary["brand"] as? String ?? ""
        let price1 =  dictionary["price1"] as? Float ?? 0.0
        let price2 =  dictionary["price2"] as? Float ?? 0.0
        let price3 =  dictionary["price3"] as? Float ?? 0.0
        let weight1 =  dictionary["weight1"] as? String ?? ""
        let weight2 =  dictionary["weight2"] as? String ?? ""
        let weight3 =  dictionary["weight3"] as? String ?? ""
        let imageUrl =  dictionary["imageUrl"] as? String ?? ""
        let selectedOption = dictionary["selectedOption"] as? Int ?? 00
        let count= dictionary["count"] as? Int ?? 00

        self.init(id: id,
                  name: name,
                  brand: brand,
                  price1: price1,
                  price2: price2,
                  price3: price3,
                  weight1: weight1,
                  weight2: weight2,
                  weight3: weight3,
                  imageUrl: imageUrl,
                  selectedOption: selectedOption,
                  count: count)
    }

}

回答1:


Imagine you have just one section of all your cart items. What would you have as your model?

  • A title
  • An array of cart items

Right?


Okay if the above makes sense, you then would need multiple of that said model type. Right again? Now break that into more specific terms. Like actual model type. It's always good and recommended to start with value type for the models unless you really need the reference semantics.

struct CartItem {
    // intentionally renamed the ItemList to CartItem and stripped out most of the properties
    let name: String
    let brand: String
}
struct Section {
    let title: String
    let cartItems: [CartItem]
}

I'm not going to initialize the array of Sections by myself, it's more of an implementation detail. But if you have to initialize that from external types (such as JSON) you may need the help of map(:), compactMap(:) or reduce(into:_:). Basically you will need the idea of Transforming a Collection in general. Or maybe Codable too.


So how do you use the above Section type with your UITableView? Look closely:

class CartViewController: UIViewController {
    . . .
    // left out for the reader to initialize on his own
    let sections = [Section]()
    . . .
    . . .
}
extension CartViewController: UITableViewDelegate, UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int {
        return sections.count
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return sections[section].count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        . . .
        let cartItem = sections[indexPath.section].cartItems[indexPath.row]
        . . .
    }
}

Now every other places in your CartViewController you will access the sections array and you will always be pointing to the right object.


Diving deep into the model manipulation. You can have different helper functions in your model type itself for different representations. Like:

struct Section {
    let title: String
    let cartItems: [CartItem]

    static func groupedSectionsByBrand(from cartItems: [CartItem]) -> [Section] {
        let grouped = Dictionary(grouping: cartItems) { $0.brand }
        let sections = grouped.map { Section(title: $0.key, cartItems: $0.value) }
        return sections
    }
}

Then you will be able to generate your desired array of Section by passing only the array of CartItem like:

let cartItems = [CartItems]() // have these values from elsewhere
let sections = Section.groupedSectionsByBrand(from: cartItems)


来源:https://stackoverflow.com/questions/59558904/how-to-fix-the-separation-collections-of-cells-in-my-sections-in-my-tableview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!