我有一个表格视图。当我单击一个单元格时,它会转到我可以编辑数据的页面,它会传递来自单元格的所有信息。然后我可以点击"保存" /"删除"并将更改发布到/从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...")
}
//
///////////////////////////////////////
}
有谁知道为什么会出现这种奇怪的功能以及如何解决它?
提前感谢您提出任何想法
答案 0 :(得分:0)
解决方案:
从save()
功能
keyboardWillHide
<强>推理强>
如果它有助于其他人,则问题是keyboardWillHide
方法会调用save()
。如果保存意味着发布到固定密钥,这不是问题,实际上甚至可能非常方便,因为每次完成编辑字段时,键盘都会隐藏并将数据保存到firebase。 但是,因为我们每次都发布到唯一键(itemID
)而不是相同的键,这会生成重复调用,以便创建密钥并将其保存到firebase。然后,使用Table View返回控制器,然后加载刚刚保存到firebase的所有重复值。