问题
This has got me in a crazy-spin. I’ve been ruminating over this for quite some time, have tried every trick of the trade, but with no luck..
I will be greatly indebted if you solve my problem.
My issue is that when I divide ProductViewController into sections:-
i)The cells i.e. images are not in correct order.
ii) Add to cart button does not function properly.
Note: - If the following code seems extensive to you, leave the rest of the code, just ook at the ProductViewController - I just want to be able to divide the ProductViewController to divide into sections as per my wish with Add to cart button working properly, most probably without changing the model. I have put the lengthy code just in case and also to show what I have tried (with the help of others).
If you are very short of time, I have also asked the incomprehensive or smaller version of the issue here - UITableView with Sections Repeating cell data in all the sections, but I would still request you to read my full code below -
I will elucidate both the points later. First see my code please -
Models -
struct Product -
import UIKit
struct Product:Equatable {
let name : String
var quantity : Int
var price : Double
let imagename: UIImage
// var subTotal : Double {
//return Double(quantity) * price }
}
var productarray = [Product(name: "a", quantity: 5, price: 5.0,imagename:#imageLiteral(resourceName: "blue")),
Product(name: "b", quantity: 10, price: 10.0, imagename:#imageLiteral(resourceName: "CakeImage")),Product(name: "c", quantity: 5, price: 5.0,imagename:#imageLiteral(resourceName: "vectorlogo")),
Product(name: "d", quantity: 10, price: 10.0, imagename:#imageLiteral(resourceName: "PeasImge")),Product(name: "e", quantity: 5, price: 5.0,imagename:#imageLiteral(resourceName: "castle")),
Product(name: "f", quantity: 10, price: 10.0, imagename:#imageLiteral(resourceName: "scoobydoo")),Product(name: "g", quantity: 5, price: 5.0,imagename:#imageLiteral(resourceName: "ufo")),
Product(name: "h", quantity: 10, price: 10.0, imagename:#imageLiteral(resourceName: "wolfsky")),Product(name: "i", quantity: 10, price: 10.0, imagename:#imageLiteral(resourceName: "universe")),
Product(name: "j", quantity: 10, price: 10.0, imagename:#imageLiteral(resourceName: "werewolf")),Product(name: "k", quantity: 10, price: 10.0, imagename:#imageLiteral(resourceName: "galaxy"))]
class CartItem -
import Foundation
class CartItem {
var quantity : Int = 1
var product : Product
// var subTotal : Float { get { return Float(product.price) * Float(quantity) } }
init(product: Product) {
self.product = product
}
}
class Cart -
import Foundation
class Cart {
var items : [CartItem] = []
}
extension Cart {
/* var total: Float {
get { return items.reduce(0.0) { value, item in
value + item.subTotal
}
}
}*/
var totalQuantity : Int {
get { return items.reduce(0) { value, item in
value + item.quantity
}
}
}
func updateCart(with product: Product) {
if !self.contains(product: product) {
self.add(product: product)
} else {
self.remove(product: product)
}
}
func updateCart() {
for item in self.items {
if item.quantity == 0 {
updateCart(with: item.product)
}
}
}
func add(product: Product) {
let item = items.filter { $0.product == product }
if item.first != nil {
item.first!.quantity += 1
} else {
items.append(CartItem(product: product))
}
}
func remove(product: Product) {
guard let index = items.firstIndex(where: { $0.product == product }) else { return}
items.remove(at: index)
}
func contains(product: Product) -> Bool {
let item = items.filter { $0.product == product }
return item.first != nil
}
}
class ProductViewController -
import UIKit
class ProductViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let sections = ["Section A", "Section B","Section C", "Section D","Section E"]
let rowspersection = [2,3,2,2,2]
fileprivate var cart = Cart()
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self }
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//Workaround to avoid the fadout the right bar button item
self.navigationItem.rightBarButtonItem?.isEnabled = false
self.navigationItem.rightBarButtonItem?.isEnabled = true
//Update cart if some items quantity is equal to 0 and reload the product table and right button bar item
cart.updateCart()
self.navigationItem.rightBarButtonItem?.title = "Checkout (\(cart.items.count))"
tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showCart" {
if let cartViewController = segue.destination as? CartViewController {
cartViewController.cart = self.cart
}
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rowspersection[section]
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ProductTableViewCell") as! ProductTableViewCell
cell.delegate = self
var index = indexPath.row
if indexPath.section != 0, rowspersection.count > indexPath.section - 1{
index += rowspersection[indexPath.section - 1]
}
if index < productarray.count {
let data = productarray[index]
cell.name?.text = data.name
cell.imageView?.image = data.imagename
let product = productarray[index]
cell.setButton(state: self.cart.contains(product: product))
}
return cell
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch(section) {
case 0:return "Section A"
case 1:return "Section B"
case 2:return "Section C"
case 3:return "Section D"
case 4:return "Section E"
default :return ""
}
}
}
extension ProductViewController: CartDelegate {
// MARK: - CartDelegate
func updateCart(cell: ProductTableViewCell) {
guard let indexPath = tableView.indexPath(for: cell) else { return }
var index = indexPath.row
if indexPath.section != 0, rowspersection.count > indexPath.section - 1{
index += rowspersection[indexPath.section - 1]
}
if index < productarray.count{
let product = productarray[index]
//Update Cart with product
cart.updateCart(with: product)
self.navigationItem.rightBarButtonItem?.title = "Checkout (\(cart.items.count))"
}
}
}
class ProductTableViewCell -
import UIKit
protocol CartDelegate {
func updateCart(cell: ProductTableViewCell) }
class ProductTableViewCell: UITableViewCell {
weak var myParent:ProductViewController?
@IBOutlet weak var name: UILabel!
@IBOutlet weak var price: UILabel!
@IBOutlet weak var imagename: UIImageView!
@IBOutlet weak var addToCartButton: UIButton!
var delegate: CartDelegate?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
addToCartButton.layer.cornerRadius = 5
addToCartButton.clipsToBounds = true
}
func setButton(state: Bool) {
addToCartButton.isSelected = state
addToCartButton.backgroundColor = (!addToCartButton.isSelected) ? .black : .red
}
@IBAction func addToCart(_ sender: Any) {
setButton(state: !addToCartButton.isSelected)
self.delegate?.updateCart(cell: self)
}
}
class CartViewController -
import UIKit
class CartViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var totalView: UIView!
@IBOutlet weak var totalLabel: UILabel!
var cart: Cart? = nil
fileprivate let reuseIdentifier = "CartItemCell"
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView(frame: .zero)
}
}
extension CartViewController: UITableViewDelegate, UITableViewDataSource {
// MARK: - Table view data source
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return (cart?.items.count)!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! CartItemTableViewCell
if let cartItem = cart?.items[indexPath.item] {
cell.delegate = self as CartItemDelegate
// cell.nameLabel.text = cartItem.productarray.name
// cell.priceLabel.text = cartItem.product.displayPrice()
// cell.quantityLabel.text = String(describing: cartItem.quantity)
cell.quantity = cartItem.quantity
}
return cell
}
}
extension CartViewController: CartItemDelegate {
// MARK: - CartItemDelegate
func updateCartItem(cell: CartItemTableViewCell, quantity: Int) {
guard let indexPath = tableView.indexPath(for: cell) else { return }
guard let cartItem = cart?.items[indexPath.row] else { return }
//Update cart item quantity
cartItem.quantity = quantity
}
}
class CartItemTableViewCell -
import UIKit
protocol CartItemDelegate {
func updateCartItem(cell: CartItemTableViewCell, quantity: Int) }
class CartItemTableViewCell: UITableViewCell {
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var priceLabel: UILabel!
@IBOutlet weak var incrementButton: UIButton!
@IBOutlet weak var decrementButton: UIButton!
@IBOutlet weak var quantityLabel: UILabel!
var delegate: CartItemDelegate?
var quantity: Int = 1
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
incrementButton.layer.cornerRadius = 10
incrementButton.clipsToBounds = true
decrementButton.layer.cornerRadius = 10
decrementButton.clipsToBounds = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
@IBAction func updateCartItemQuantity(_ sender: Any) {
if (sender as! UIButton).tag == 0 {
quantity = quantity + 1
} else if quantity > 0 {
quantity = quantity - 1
}
decrementButton.isEnabled = quantity > 0
decrementButton.backgroundColor = !decrementButton.isEnabled ? .gray : .black
self.quantityLabel.text = String(describing: quantity)
self.delegate?.updateCartItem(cell: self, quantity: quantity)
}
}
The result is this. First see the image please, and then I will explain what's fallacious
1)Images/cells in Model “struct Product” are in order - blue, CakeImage,vectorlogo, PeasImge, castle, scoobydoo, ufo, wolfsky, universe, werewolf, galaxy
but the images in Simulator (see image above) are in erratic pattern -
section A:blue,CakeImage, vectorlogo
section B: vectorlogo, PeasImge, castle.
section C:PeasImge, castle.
section D:PeasImge, castle.
section E:PeasImge, castle.
- Just the images are not erratic, the Add button on the cells also act erratically - Clicking on Add button in lower sections sometimes increase and decreases the rightBarButtonItem i.e. Chekcout(4)
I had asked a similar version of the problem earlier where a user PGDev had suggested -
instead of making different arrays, to create a single array of custom type and use that as the dataSource of your tableView. Example:
struct Section {
let name: String
let products: [Product]
}
let sections = [
Section(name: "Section A", products: [Product(name: "a", quantity: 5, price: 5.0,imagename:#imageLiteral(resourceName: "blue")),
Product(name: "b", quantity: 10, price: 10.0, imagename:#imageLiteral(resourceName: "CakeImage"))]),
Section(name: "Section B", products: [Product(name: "c", quantity: 5, price: 5.0,imagename:#imageLiteral(resourceName: "vectorlogo")),
Product(name: "d", quantity: 10, price: 10.0, imagename:#imageLiteral(resourceName: "PeasImge")),Product(name: "e", quantity: 5, price: 5.0,imagename:#imageLiteral(resourceName: "castle"))]),
Section(name: "Section C", products: [Product(name: "f", quantity: 10, price: 10.0, imagename:#imageLiteral(resourceName: "scoobydoo")),Product(name: "g", quantity: 5, price: 5.0,imagename:#imageLiteral(resourceName: "ufo"))]),
Section(name: "Section D", products: [Product(name: "h", quantity: 10, price: 10.0, imagename:#imageLiteral(resourceName: "wolfsky")),Product(name: "i", quantity: 10, price: 10.0, imagename:#imageLiteral(resourceName: "universe"))]),
Section(name: "Section E”, products: [ Product(name: "j", quantity: 10, price: 10.0, imagename:#imageLiteral(resourceName: "werewolf")),Product(name: "k", quantity: 10, price: 10.0, imagename:#imageLiteral(resourceName: "galaxy"))]])
]
i.e to use sections array as the dataSource to avoid a lot of confusion.
I am ready to do that as well and maybe this method is better but then I will have to change many parts of the code. How to go about that?
I just want to achieve the following targets-
Dividing the cells/images of ProdutViewcontroller into sections as I wish.
Add data to rightBarButtonItem i.e. Checkout correctly which should subsequently be transferred to CartViewController.
.. Make no haste. Take your own time to straighten out the issues but its crucial for me to fix them as I need similar code for a production app . I’m in dilema. Please bail me out. I’ll be much obliged.
来源:https://stackoverflow.com/questions/64043529/swift-how-to-divide-tableview-into-sections-with-add-to-cart-working-properly