函数触发先前打开的VC

时间:2017-08-28 12:03:28

标签: swift uitableview firebase firebase-realtime-database

我有一个表格视图。当我单击一个单元格时,它会转到我可以编辑数据的页面,它会传递来自单元格的所有信息。然后我可以点击"保存" /"删除"并将更改发布到/从firebase删除项目。然后它将我带回到表视图并提取新更新的firebase数据。

问题

但是,如果我删除一个项目(在我可以编辑/删除详细信息的页面上),然后转换回表格视图,然后单击另一个单元格并编辑并保存,它会发布已编辑的项目 AND 以前删除的项目。它对我来说甚至没有意义,这是可能的。

研究

我尝试删除某个项目,删除该应用,重新开启,然后保存不同的单元格。在这种情况下,不会发生错误。这让我相信可能存在某种内存/内存泄漏问题,但我不确定如何才能真正解决此问题。

我的尝试

使用TableView的ViewController

import UIKit
import Firebase

class ItemListVC: UIViewController, UITableViewDelegate, UITableViewDataSource, UIPickerViewDelegate {

    //reference to firebase database
    var ref : DatabaseReference?
    var handle : DatabaseHandle?
    var allItemData : [String:Any] = [:]
    // Get current userID
    let userID = (Auth.auth().currentUser?.uid) ?? ""
    // Outlets
    @IBOutlet weak var addItemOutlet: UIButton!
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var numberOfItemsTextBox: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Set up tableView
        tableView.delegate = self
        tableView.dataSource = self
        tableView.rowHeight = 69
        // Set up UIPickerView (dropdown for numberOfItemsTextBox)
        setUpPickers()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        // fetch user's item data from firebase
        ref = Database.database().reference()
        handle = ref?.child("users").child(userID).child("items").observe(DataEventType.value, with: { (snapshot) in
            // replace the old array
            self.allItemData = snapshot.value as? [String : AnyObject] ?? [:]
            // reload the UITableView
            self.tableView.reloadData()
            self.setEnableForAddOutlet()
        })
        // Get numberOfItems
        ref.child("user/" + userID + "/items/numberOfItems").observeSingleEvent(of: .value, with: { (snapshot) in
            if snapshot.value is NSNull {
                self.numberOfItemsTextBox.text = ""
            } else {
                // successfully fetched value
                value = (snapshot.value as? NSDictionary)!
                self.numberOfItemsTextBox.text = numberOfItems
            }
        })
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func save() {
        print("post the value for numberOfItems to firebase")
        let itemsToPost : [String: Any] = [
            "numberOfItems" : numberOfItemsTextBox.text ?? "",
        ]
        for item in items {
            self.ref.child("users/" + userID + "/items").child(item.key).setValue(item.value)
        }
    }

    // Action when the return key pressed
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        print("running textFieldShouldReturn")
        textField.resignFirstResponder()
        return true
    }

    func setEnableForAddOutlet() {
        if (allItemData.count > 1) {
            addItemOutlet.isEnabled = false
        } else {
            addItemOutlet.isEnabled = true
        }
    }

    ////////////////
    // BUTTONS
    @IBAction func addItemButtonPressed(_ sender: Any) {
        // Save
        save()
        performSegue(withIdentifier:"ItemAdd", sender: nil)
    }

    @IBAction func nextButtonPressed(_ sender: Any) {
        // Save
        save()
        performSegue(withIdentifier:"mySegueToADifferentPlace", sender: nil)
    }

    @IBAction func doneButtonPressed(_ sender: Any) {
        // Save
        save()
        performSegue(withIdentifier:"mySegueToSomeWhereElse", sender: nil)
    }

    @IBAction func backButtonPressed(_ sender: Any) {
        // Save
        save()
        performSegue(withIdentifier:"mySegueToSomeWhereElse", sender: nil)
    }

    // End of [BUTTONS]
    ////////////////

    ////////////////
    // TABLE VIEW

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! itemListCell
        let key = Array(allItemData.keys)[indexPath.row]
        let item = (allItemData[key] as? [String:Any]) ?? [:]
        let name = (item["name"] as? String) ?? ""
        let year = (item["year"] as? String) ?? ""
        cell.titleLabel?.text = name
        cell.itemID = key
        cell.name = name
        cell.year = year
        cell.other = (item["other"] as? String) ?? ""
        cell.state = (item["state"] as? String) ?? ""

        return cell
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
        performSegue(withIdentifier:"ItemEdit", sender: nil)
    }

    // End of [TABLE VIEW]
    ////////////////


    //////////
    // Picker Controllers


    //Arrays for picker views
    let numOfItemsArray = ["0", "1", "2", "2+"]
    let errorArray = ["unable to load"]

    let itemPickerView = UIPickerView()

    func setUpPickers() {
        print("running setUpPickers")
        // new picker
        itemPickerView.delegate = self
        numberOfItemsTextBox.inputView = itemPickerView
    }

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        print("setting titleForRow...")
        if pickerView == itemPickerView {
            return numOfItemsArray[row]
        } else {
            return errorArray[0]
        }
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        print("setting numberOfRowsInComponent...")
        if pickerView == itemPickerView {
            return numOfItemsArray.count
        } else {
            return errorArray.count
        }
    }

    func numberOfComponents(in pickerView: UIPickerView) -> Int{
        print("setting numberOfComponents...")
        return 1
    }

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        print("setting didSelectRow...")

        if pickerView == itemPickerView {
            //change the text in the textField equal to the selection
            let value = numOfItemsArray[row]
            numberOfItemsTextBox.text = value
            if value == numOfItemsArray[0] {
                numberOfItemsTextBox.textColor = UIColor.lightGray
            } else {
                numberOfItemsTextBox.textColor = UIColor.black
            }
        }
        numberOfItemsTextBox.resignFirstResponder()
    }

    //
    //////////


    ///////////////////
    //MARK: - Navigation
    //In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "ItemEdit" {
            // get the indexPath of the selected cell
            if let indexPath = tableView.indexPathForSelectedRow {
                // get the selected cell
                let cell = tableView.cellForRow(at: indexPath) as! itemListCell
                // get an instance of the next ViewController
                let itemController : EditVC = segue.destination as! EditVC
                // take all the data from the selected cell and pass it to the next ViewController
                print("itemController variables")
                itemController.itemID = cell.itemID
                itemController.name = cell.name
                itemController.year = cell.year
                itemController.other = cell.other
                itemController.state = cell.state
            } else {
                // tell the user if the indexPath of the selected cell cannot be retrieved
                print("Unable to select item")
            }
        }
    }
    //////////////////
}

要编辑/删除的ViewController

import UIKit
import Firebase

class EditVC: UIViewController, UIPickerViewDelegate {

    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var nameTextField: UITextField!
    @IBOutlet weak var yearTextField: UITextField!
    @IBOutlet weak var otherTextField: UITextField!
    @IBOutlet weak var stateTextField: UITextField!

    // Variables for items's Details
    var itemID = ""
    var name = ""
    var year = ""
    var other = ""
    var state = ""

    let userID = Auth.auth().currentUser!.uid
    let ref = Database.database().reference()


    override func viewDidLoad() {
        super.viewDidLoad()
        self.hideKeyboardWhenTappedAround()
        //for shifting textFields when keyboard covers
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name:NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name:NSNotification.Name.UIKeyboardWillHide, object: nil)

        setUpPickers()

        // Set initial values for TextFields
        nameTextField.text = name
        yearTextField.text = year
        otherTextField.text = other
        stateTextField.text = state
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    /////////////////////////
    // Buttons
    @IBAction func returnButtonPressed(_ sender: Any) {
        save()
        performSegue(withIdentifier: "backToTableView", sender: self)
    }

    @IBAction func deleteButtonPressed(_ sender: Any) {
        if itemID != "" {
            print("removing item")
            self.ref.child("users").child(userID).child("items").child(itemID).removeValue();
        }
        performSegue(withIdentifier: "backToTableView", sender: self)
    }

    @IBAction func doneButton(_ sender: Any) {
        save()
        performSegue(withIdentifier: "backToTableView", sender: self)
    }
    // End of [Buttons]
    /////////////////////////

    func save() {
        // Create an items object to post
        // Get the values from all textfields and set in the object
        let itemsToPost : [String: Any] = [
            "name": nameTextField.text ?? "",
            "year": yearTextField.text ?? "",
            "state": stateTextField.text ?? "",
            "other": otherTextField.text ?? ""
        ]
        // if the user is saving a new item (itemID == ""), then create a new ID and set itemID
        if (itemID == "") {
            itemID = ref.child("users/" + userID + "/items").childByAutoId().key
        }
        // post the items object to firebase
        for item in items {
            self.ref.child("users/" + userID + "/items/" + itemID).child(item.key).setValue(item.value)
        }
    }

    //////////
    // Picker Controllers

    //Arrays for picker views
    let stateArray = ["State", "AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL", "GA", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY"]
    var yearArray : [String] = []
    let errorArray = ["unable to load"]

    let yearPickerView = UIPickerView()
    let statePickerView = UIPickerView()

    func setUpPickers() {
        print("running setUpPickers")
        // new picker
        yearPickerView.delegate = self
        yearTextField.inputView = yearPickerView
        statePickerView.delegate = self
        stateTextField.inputView = statePickerView

        var i = 1900
        let date = Date()
        let calendar = Calendar.current
        let maxYear = calendar.component(.year, from: date)
        while i < (maxYear + 1) {
            yearArray.insert(String(describing: i), at: 0)
            i += 1
        }
    }

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        print("setting titleForRow...")
        if pickerView == statePickerView {
            return stateArray[row]
        } else if pickerView == yearPickerView {
            return yearArray[row]
        } else {
            return errorArray[0]
        }
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        print("setting numberOfRowsInComponent...")
        if pickerView == statePickerView {
            return stateArray.count
        } else if pickerView == yearPickerView {
            return yearArray.count
        } else {
            return errorArray.count
        }
    }

    public func numberOfComponents(in pickerView: UIPickerView) -> Int{
        print("setting numberOfComponents...")
        return 1
    }

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        print("setting didSelectRow...")

        if pickerView == statePickerView {
            print("setting text of stateTextField")
            stateTextField.text = stateArray[row]

            if stateTextField.text == stateArray[0] {
                stateTextField.textColor = UIColor.lightGray
            } else {
                stateTextField.textColor = UIColor.black
            }
        } else if pickerView == yearPickerView {
            print("setting text of yearTextField")
            yearTextField.text = yearArray[row]

            if yearTextField.text == stateArray[0] {
                yearTextField.textColor = UIColor.lightGray
            } else {
                yearTextField.textColor = UIColor.black
            }
        }
    }

    //
    //////////


    ///////////////////////////////////////
    // shifting text fields (add listeners in viewDidLoad)
    func keyboardWillShow(notification:NSNotification){
        print("running keyboardWillShow...")
        //give room at the bottom of the scroll view, so it doesn't cover up anything the user needs to tap
        var userInfo = notification.userInfo!
        var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        keyboardFrame = self.view.convert(keyboardFrame, from: nil)

        var contentInset:UIEdgeInsets = self.scrollView.contentInset
        contentInset.bottom = (keyboardFrame.size.height + 60)
        self.scrollView.contentInset = contentInset
    }

    func keyboardWillHide(notification:NSNotification){
        print("running keyboardWillHide...")
        let contentInset:UIEdgeInsets = UIEdgeInsets.zero
        self.scrollView.contentInset = contentInset

        print("saving...")
        save()
        print("finished keyboardWillHide...")
    }
    //
    ///////////////////////////////////////
}

有谁知道为什么会出现这种奇怪的功能以及如何解决它?

提前感谢您提出任何想法

1 个答案:

答案 0 :(得分:0)

解决方案:

save()功能

中删除keyboardWillHide

<强>推理

如果它有助于其他人,则问题是keyboardWillHide方法会调用save()。如果保存意味着发布到固定密钥,这不是问题,实际上甚至可能非常方便,因为每次完成编辑字段时,键盘都会隐藏并将数据保存到firebase。 但是,因为我们每次都发布到唯一键(itemID)而不是相同的键,这会生成重复调用,以便创建密钥并将其保存到firebase。然后,使用Table View返回控制器,然后加载刚刚保存到firebase的所有重复值。