首页 > 解决方案 > 有没有办法从 Swift 字典中的查询中保存我的 firebase 模型?

问题描述

首先我想告诉你我的目标是什么:

我的应用程序用户应该可以点击每个 ProductTableViewCell 中的 + 或 - 步进器来将产品添加到购物车或从购物车中删除。在嵌入 productTableView 的 NewOrderViewController 中,应该有一个标签,用于向用户显示购物车中有多少产品。我将其命名为 totalProductCountLabel。还应该有一个名为 totalProductPriceLabel 的标签,以显示购物车中所有产品的价格。每次用户在单元格内点击步进器时都应该更新购物车,并且当 product.counter 值为 0 时,产品应该自动从购物车中删除。

昨天我有一个完美的解决方案:

视图控制器

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, CartSelection {

var totalProductCount = 0
var timerEnabled = true
var timerCount = 0

@IBOutlet weak var testTableview: UITableView!

@IBOutlet weak var totalProductCountLabel: UILabel!
@IBOutlet weak var totalProductPriceLabel: UILabel!

var productArray = [Product]()
var cartDictionary = [String: Product]()

override func viewDidLoad() {
    super.viewDidLoad()
    testTableview.delegate = self
    testTableview.dataSource = self
    testTableview.allowsSelection = false

    productArray.append(Product(name: "Bananen", count: 0, price: 1.09, uuid: UUID().uuidString))
    productArray.append(Product(name: "Äpfel", count: 0, price: 0.81, uuid: UUID().uuidString))
    productArray.append(Product(name: "Kirschen", count: 0, price: 6.34, uuid: UUID().uuidString))
    productArray.append(Product(name: "Tomaten", count: 0, price: 2.68, uuid: UUID().uuidString))
    productArray.append(Product(name: "Weintrauben", count: 0, price: 1.48, uuid: UUID().uuidString))
    productArray.append(Product(name: "Schokolade", count: 0, price: 0.67, uuid: UUID().uuidString))

    testTableview.reloadData()
    timerEnabled = true
    timerCount = 0
    self.totalProductCountLabel.text = "Anzahl Produkte: 0 Stück"
    self.totalProductPriceLabel.text = "Preis Produkte: 0.00 €"
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    timerEnabled = false
    timerCount = 0
}

func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    timerEnabled = false
    timerCount = 5
}

func startTimer() {
    _ = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { timer in
        if self.timerEnabled != false && self.timerCount < 4 {
            self.testTableview.reloadData()
            print("Die Produktliste wurde aktualisiert!")
            self.timerCount += 1
        } else {
            timer.invalidate()
            self.timerEnabled = true
            self.timerCount = 0
        }
    }
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return productArray.count
}

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

    cell.product = productArray[indexPath.row]
    cell.productNameLabel.text = "\(cell.product.name)"
    cell.productPriceLabel.text = "\(cell.product.price)" + " € / kg"
    cell.productCountLabel.text = "\(cell.product.count)"
    cell.cartSelectionDelegate = self

    return cell
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 188
}

func addProductToCart(product: Product, uuid: String) {
    timerEnabled = false
    cartDictionary[uuid] = product
    if cartDictionary[uuid]?.count == 0 {
        cartDictionary.removeValue(forKey: uuid)
    }
    calculateTotal()
}

func calculateTotal() {
    var totalProductCount = 0
    var totalProductPrice = 0.00

    for (_,value) in cartDictionary {
        totalProductCount += value.count
        totalProductPrice += value.totalPrice
    }
    self.totalProductCountLabel.text = "Anzahl Produkte: \(totalProductCount) Stück"
    self.totalProductPriceLabel.text = "Preis Produkte: \(String(format: "%5.2f", totalProductPrice)) €"
    self.totalProductCount = totalProductCount

    if cartDictionary.isEmpty {
        print("Der Einkaufswagen ist leer")
        timerEnabled = true
        timerCount = 0
        startTimer()
    }
}

@IBAction func orderButtonTapped(_ sender: UIButton) {
    if totalProductCount != 0 {
        showOrderConfirmation()
        var priceLabel = 0.00
        print("Im Einkaufwagen sind folgende Produkte:")
        for (key,value) in cartDictionary {
            priceLabel += value.totalPrice
            print("Produkt: \(value.name) --> \(value.count)  __ Produktkennung: \(key)")
        }
        print("Gesamtpreis: \(priceLabel)")
    }
}


func showOrderConfirmation() {
    let alert = UIAlertController(title: "Bestellung aufgeben?", message: nil, preferredStyle: .alert)
    let okayAction = UIAlertAction(title: "Bestellung aufgeben", style: .default)
    let cancelAction = UIAlertAction(title: "Bestellung bearbeiten", style: .destructive)
    alert.addAction(okayAction)
    alert.addAction(cancelAction)
    present(alert, animated: true, completion: nil)
}
}

产品型号

import UIKit

struct Product {
var name = "Produkt"
var count = 0
var price = 0.00
var totalPrice: Double {
    return Double((count)) * (price)
}
var uuid = "Example: 20EE8409-230C-4E69-B366-C2EEE03998AF"
}

protocol CartSelection {
func addProductToCart(product : Product, uuid : String)
}

表视图单元格

import UIKit

class TestTableViewCell: UITableViewCell {

var cartSelectionDelegate: CartSelection?

@IBOutlet weak var productCountLabel: UILabel!
@IBOutlet weak var productNameLabel: UILabel!
@IBOutlet weak var productPriceLabel: UILabel!
@IBOutlet weak var totalProductPriceLabel: UILabel!
@IBOutlet weak var stepper: UIStepper!

@IBAction func cellTrashButtonTapped(_ sender: UIButton) {
    resetCell()
}

@IBAction func changeProductCountStepper(_ sender: UIStepper) {

    productCountLabel.text = Int(sender.value).description
    product.count = Int(sender.value)
    let totalPriceForProduct = Double(product.count) * product.price
    totalProductPriceLabel.text = "Produktgesamtpreis: \(String(format: "%5.2f", totalPriceForProduct)) €"
    updateChanges()
}

func updateChanges() {
    cartSelectionDelegate?.addProductToCart(product: product, uuid: product.uuid)
}

func resetCell() {
    productCountLabel.text = "0"
    product.count = 0
    totalProductPriceLabel.text = "Produktgesamtpreis: 0.00 €"
    stepper.value = 0
    updateChanges()
}

override func awakeFromNib() {
    super.awakeFromNib()
    stepper.autorepeat = true

}

var product : Product!
var productIndex = 0
}

现在我的产品存储在 Firebase Firestore 中,因此我不再需要 append 方法,而是使用查询。所有工作如前,我的产品显示在表中。

第一个问题: 当按下步进器时,我的 ViewController 的 Outlets 全部为零,所以我再也看不到我的标签计数了。(ViewController 中的那个),单元格中的标签正在工作。

第二个问题: 在第一个代码中,我使用字典来存储其中的所有产品(“cartDictionary”)。这也适用于第二个代码。但仅限于一种产品。如果我只点击一个单元格步进器,字典中的计数器正在更新,但如果我点击另一个单元格步进器,整个字典是空的,它会再次从另一个产品开始。

我找不到这个问题的解决方案,我知道问题是我的 ViewController 在使用我的插座时没有初始化,但是如果我初始化它,我的 dataSource 会崩溃。

如果有人帮助我,那就太好了,因为我最后几天不能继续工作了。

非常感谢!

我的代码不起作用:

产品型号

import UIKit
import FirebaseFirestore

protocol CartSelection {
func addProductToCart(product : Product, uuid : String)
}

struct Product {

var documentID: String
var shopID: String
var productName: String
var productPrice: Double
var counter: Int
var uuid: String
var totalPrice: Double {
    return Double((counter)) * (productPrice)
}
}

// MARK: - Firestore interoperability

extension Product: DocumentSerializable {

/// Initializes a shop with a documentID auto-generated by Firestore.
init(shopID: String,
     productName: String,
     productPrice: Double,
     counter: Int,
     uuid: String) {
    let document = Firestore.firestore().shops.document()
    self.init(documentID: document.documentID,
              shopID: shopID,
              productName: productName,
              productPrice: productPrice,
              counter: counter,
              uuid: uuid)
}

/// Initializes a shop from a documentID and some data, from Firestore.
private init?(documentID: String, dictionary: [String: Any]) {
    guard let shopID = dictionary["shopID"] as? String,
        let productName = dictionary["productName"] as? String,
    let productPrice = dictionary["productPrice"] as? Double,
        let counter = dictionary["counter"] as? Int,
    let uuid = dictionary["uuid"] as? String else { return nil }

    self.init(documentID: documentID,
              shopID: shopID,
              productName: productName,
              productPrice: productPrice,
              counter: counter,
              uuid: uuid)
}

init?(document: QueryDocumentSnapshot) {
    self.init(documentID: document.documentID, dictionary: document.data())
}

init?(document: DocumentSnapshot) {
    guard let data = document.data() else { return nil }
    self.init(documentID: document.documentID, dictionary: data)
}

/// The dictionary representation of the restaurant for uploading to Firestore.
var documentData: [String: Any] {
    return [
        "shopID": shopID,
        "productName": productName,
        "productPrice": productPrice,
        "counter": counter,
        "uuid": uuid
    ]
}
}

产品 TableViewCell

import UIKit
import FirebaseFirestore

class ProductTableViewCell: UITableViewCell {

var cartSelectionDelegate: CartSelection?

@IBOutlet weak var productCountLabel: UILabel!
@IBOutlet weak var productNameLabel: UILabel!
@IBOutlet weak var productPriceLabel: UILabel!
@IBOutlet weak var totalProductPriceLabel: UILabel!
@IBOutlet weak var stepper: UIStepper!

@IBAction func cellTrashButtonTapped(_ sender: UIButton) {
    resetCell()
}

@IBAction func changeProductCountStepper(_ sender: UIStepper) {

    productCountLabel.text = Int(sender.value).description
    product.counter = Int(sender.value)
    let totalPriceForProduct = Double(product.counter) * product.productPrice
    totalProductPriceLabel.text = "Produktgesamtpreis: \(String(format: "%5.2f", totalPriceForProduct)) €"
    updateChanges()
}

func updateChanges() {
    cartSelectionDelegate?.addProductToCart(product: product, uuid: product.uuid)
}

func resetCell() {
    productCountLabel.text = "0"
    product.counter = 0
    totalProductPriceLabel.text = "Produktgesamtpreis: 0.00 €"
    stepper.value = 0
    updateChanges()
}

override func awakeFromNib() {
    super.awakeFromNib()
    stepper.autorepeat = true

}

var product : Product!
var productIndex = 0
}

产品表视图数据源

import UIKit
import FirebaseFirestore

@objc class ProductTableViewDataSource: NSObject, UITableViewDataSource {

private let products: LocalCollection<Product>
var sectionTitle: String?

public init(products: LocalCollection<Product>) {
    self.products = products
}

public convenience init(query: Query, updateHandler: @escaping ([DocumentChange]) -> ()) {
    let collection = LocalCollection<Product>(query: query, updateHandler: updateHandler)
    self.init(products: collection)
}

public func startUpdates() {
    products.listen()
}

public func stopUpdates() {
    products.stopListening()
}


subscript(index: Int) -> Product {
    return products[index]
}

public var count: Int {
    return products.count
}

// MARK: - UITableViewDataSource
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return sectionTitle
}

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

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

    cell.product = products[indexPath.row]
    cell.productNameLabel.text = "\(cell.product.productName)"
    cell.productPriceLabel.text = "\(cell.product.productPrice)" + " € / kg"
    cell.productCountLabel.text = "\(cell.product.counter)"
    cell.cartSelectionDelegate = NewOrderViewController()

    return cell
}

}

新订单视图控制器

import UIKit
import FirebaseFirestore

class NewOrderViewController: UIViewController, UITableViewDelegate, CartSelection {

@IBOutlet var productTableView: UITableView!
@IBOutlet weak var totalProductCountLabel: UILabel!
@IBOutlet weak var totalProductPriceLabel: UILabel!


private var shop: Shop!
var totalProductCount = 0
var timerEnabled = true
var timerCount = 0
var cartDictionary = [String: Product]()

// MARK: - View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()

    productTableView.dataSource = dataSource
    productTableView.delegate = self
    query = baseQuery
    productTableView.allowsSelection = false
    productTableView.reloadData()

    timerEnabled = true
    timerCount = 0
    totalProductCountLabel.text = "Anzahl Produkte: 0 Stück"
    totalProductPriceLabel.text = "Preis Produkte: 0.00 €"        
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.setNeedsStatusBarAppearanceUpdate()
    dataSource.startUpdates()

}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    dataSource.stopUpdates()
    timerEnabled = false
    timerCount = 0
}

// MARK: - DataSource and Query

private func dataSourceForQuery(_ query: Query) -> ProductTableViewDataSource {
    return ProductTableViewDataSource(query: query) { [unowned self] (changes) in
        self.productTableView.reloadData()
    }
}

lazy private var dataSource: ProductTableViewDataSource = {
    return dataSourceForQuery(baseQuery)
}()

lazy private var baseQuery: Query = {
    Firestore.firestore().products.whereField("shopID", isEqualTo: shop.documentID)
}()

fileprivate var query: Query? {
    didSet {
        dataSource.stopUpdates()
        productTableView.dataSource = nil
        if let query = query {
            dataSource = dataSourceForQuery(query)
            productTableView.dataSource = dataSource
            dataSource.startUpdates()
        }
    }
}

// MARK: - Delegate Method

func addProductToCart(product: Product, uuid: String) {
    timerEnabled = false
    cartDictionary[uuid] = product

    if cartDictionary[uuid]?.counter == 0 {
        cartDictionary.removeValue(forKey: uuid)
    }
    calculateTotal()
}

// MARK: - Actions

@IBAction func orderButtonTapped(_ sender: UIButton) {
    if totalProductCount != 0 {
        showOrderConfirmation()
        var priceLabel = 0.00
        print("Im Einkaufwagen sind folgende Produkte:")
        for (key,value) in cartDictionary {
            priceLabel += value.totalPrice
            print("Produkt: \(value.productName) --> \(value.counter)  __ Produktkennung: \(key)")
        }
        print("Gesamtpreis: \(priceLabel)")
    }
}

// MARK: - Functions

func showOrderConfirmation() {
    let alert = UIAlertController(title: "Bestellen?", message: nil, preferredStyle: .alert)
    let okayAction = UIAlertAction(title: "Bestellung aufgeben", style: .default)
    let cancelAction = UIAlertAction(title: "Bestellung bearbeiten", style: .destructive)
    alert.addAction(okayAction)
    alert.addAction(cancelAction)
    present(alert, animated: true, completion: nil)
}

func calculateTotal() {
    var totalProductCount = 0
    var totalProductPrice = 0.00

    for (_,value) in cartDictionary {
        totalProductCount += value.counter
        totalProductPrice += value.totalPrice
    }
    self.totalProductCountLabel.text = "Anzahl Produkte: \(totalProductCount) Stück"
    self.totalProductPriceLabel.text = "Preis Produkte: \(String(format: "%5.2f", totalProductPrice)) €"

    if cartDictionary.isEmpty {
        print("Der Einkaufswagen ist leer")
        timerEnabled = true
        timerCount = 0
        startTimer()
    }
}

func startTimer() {
    _ = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { timer in
        if self.timerEnabled != false && self.timerCount < 4 {
            self.productTableView.reloadData()
            print("Die Produktliste wurde aktualisiert!")
            self.timerCount += 1
        } else {
            timer.invalidate()
            self.timerEnabled = true
            self.timerCount = 0
        }
    }
}

// MARK: - TableView Delegate

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 188
}

func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    timerEnabled = false
    timerCount = 5
}

// MARK: - Navigation

static func fromStoryboard(_ storyboard: UIStoryboard = UIStoryboard(name: Constants.StoryboardID.main, bundle: nil),
                           forShop shop: Shop) -> NewOrderViewController {
    let controller = storyboard.instantiateViewController(withIdentifier: Constants.StoryboardID.newOrderViewController) as! NewOrderViewController
    controller.shop = shop
    return controller
}
}

标签: iosswift

解决方案


All problems fixed by making a static instance of my NewOrderViewController!

class NewOrderViewController: UIViewController, UITableViewDelegate, CartSelection {

@IBOutlet var productTableView: UITableView!
@IBOutlet weak var totalProductCountLabel: UILabel!
@IBOutlet weak var totalProductPriceLabel: UILabel!


private var shop: Shop!
var totalProductCount = 0
var timerEnabled = true
var timerCount = 0
var cartDictionary = [String: Product]()
static var instance: NewOrderViewController?

// MARK: - View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()

    NewOrderViewController.instance = self

    productTableView.dataSource = dataSource
    productTableView.delegate = self
    query = baseQuery
    productTableView.allowsSelection = false
    productTableView.reloadData()

    timerEnabled = true
    timerCount = 0
    totalProductCountLabel.text = "Anzahl Produkte: 0 Stück"
    totalProductPriceLabel.text = "Preis Produkte: 0.00 €"        
}

推荐阅读