#arrays #swift #uitableview #firebase-realtime-database #refresh
# #массивы #swift #uitableview #firebase-база данных в реальном времени #обновить
Вопрос:
Я пытаюсь иметь два массива, используемых в качестве источников данных для двух табличных представлений. Один массив содержит тип пользователя: macro, а другой массив содержит тип пользователя: micro.
Если тип пользователя изменен с макро на микро (и наоборот) Я хочу удалить пользователя из одной таблицы и добавить его в другую.
В настоящее время я могу обновить тег пользователя, если он изменен в базе данных Firebase, и он появится в соответствующем массиве при перезапуске приложения. Функция наблюдения собирает ее только один раз, и если она изменена, она не обновляет таблицу до тех пор, пока пользователь не выйдет из приложения и не откроет его снова. Функция, которую я использую для наблюдения.childChanged, похоже, не обновляет массивы сразу в приложении пользователя, если они не выполняют то, что было упомянуто ранее.
Моя главная проблема заключается в том, что мои таблицы не отображают пользователей в таблице. Я могу получить доступ к их узлу и пользователю, но они не отображаются в моих таблицах.
Вот код для моего пользовательского класса:
import UIKit
import Firebase
class User: NSObject {
var Name: String?
var Email: String?
var UID: String?
var Tag: String?
init?(from snapshot: DataSnapshot) {
let dictionary = snapshot.value as? [String: Any]
self.Name = dictionary!["Name"] as? String
self.Email = dictionary!["Email"] as? String
self.UID = dictionary!["UID"] as? String
self.Tag = dictionary!["Tag"] as? String
}
}
Вот мой код для загрузки и заполнения моего allUsersArray информация добавляется:
func loadAllUsersAndPopulateArray() {
let ref = Database.database().reference().child("Users")
ref.observeSingleEvent(of: .value, with: { snapshot in
let allUsersSnapshot = snapshot.children.allObjects as! [DataSnapshot]
for userSnap in allUsersSnapshot {
let user = User(from: userSnap)
self.allUsersArray.append(user!)
self.macroUsersArray = self.allUsersArray.filter { $0.Tag == "Macro" }
self.microUsersArray = self.allUsersArray.filter { $0.Tag == "Micro" }
self.observeChangeInUserProperty()
}
})
}
Вот мой код для наблюдения за изменениями в Firebase:
func observeChangeInUserProperty() {
let ref = Database.database().reference().child("Users")
ref.observe(.childChanged, with: { snapshot in
let key = snapshot.key
let tag = snapshot.childSnapshot(forPath: "Tag").value as! String // ! = never optional
//get the user from the allUsersArray by its key
if let user = self.allUsersArray.first(where: { $0.Tag == key }) {
if user.Tag != tag { //if the tag changed, handle it
user.Tag = tag //update the allUsersArray
if tag == "Macro" { //if the new tag is Macro remove the user from the Micro array
if let userIndex = self.microUsersArray.firstIndex(where: { $0.Tag == key }) {
self.microUsersArray.remove(at: userIndex)
self.macroUsersArray.append(user) //add user to macro array
}
} else { //new type is micro so remove from macro array
if let userIndex = self.macroUsersArray.firstIndex(where: { $0.Tag == key }) {
self.macroUsersArray.remove(at: userIndex)
self.microUsersArray.append(user)
}
}
//reload the tableviews to reflect the changes
self.tableView.reloadData()
self.microTableView.reloadData()
}
}
})
}
Вот мой код для контроллера:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var allUsersArray = [User]()
var macroUsersArray = [User]()
var microUsersArray = [User]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
searchBar.delegate = self
tableView.register(UserCell.self, forCellReuseIdentifier: networkCell)
microTableView.delegate = self
microTableView.dataSource = self
microTableView.register(microCell.self, forCellReuseIdentifier: microCell)
loadAllUsersAndPopulateArray()
}
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
if (tableView === self.tableView) {
return 1
}
else if (tableView === self.microTableView) {
// Do something else
return 1
}
fatalError("Invalid table")
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (tableView === self.tableView) {
let cell = tableView.dequeueReusableCell(withIdentifier: networkCell, for: indexPath) as! UserCell
let user = macroUsersArray[indexPath.row]
cell.textLabel?.text = user.Name
return cell
} else if (tableView === self.microTableView) {
let cell = tableView.dequeueReusableCell(withIdentifier: microCell, for: indexPath) as! microInfluencerCell
let user = microUsersArray[indexPath.row]
cell.textLabel?.text = user.Name
return cell
} else {
fatalError("Invalid table")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (tableView === self.tableView) {
return macroUsersArray.count
}
else if (tableView === self.microTableView) {
return microUsersArray.count
}
else {
fatalError("Invalid table")
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if (tableView === self.tableView) {
return 72
}
else if (tableView === self.microTableView) {
return 72
}
else {
fatalError("Invalid table")
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if (tableView === self.tableView) {
dismiss(animated: true) {
let userName = self.macroUsersArray[indexPath.row]
self.showSecondViewController(user: userName)
print("Dismiss completed")
}
}
else if (tableView === self.microTableView) {
dismiss(animated: true) {
let userName = self.microUsersArray[indexPath.row]
self.showSecondController(user: userName)
print("Dismiss completed")
}
}
else {
fatalError("Invalid table")
}
}
}
func showSecondViewController(user: User) {
let SecondViewController = SecondViewController()
SecondViewController.user = user
let navigationController = UINavigationController(rootViewController: SecondViewController)
self.present(navigationController, animated: true, completion: nil)
}
func loadAllUsersAndPopulateArray() {
let ref = Database.database().reference().child("Users")
ref.observeSingleEvent(of: .value, with: { snapshot in
let allUsersSnapshot = snapshot.children.allObjects as! [DataSnapshot]
for userSnap in allUsersSnapshot {
let user = User(from: userSnap)
self.allUsersArray.append(user!)
self.macroUsersArray = self.allUsersArray.filter { $0.Tag == "Macro" }
self.microUsersArray = self.allUsersArray.filter { $0.Tag == "Micro" }
self.observeChangeInUserProperty()
}
})
}
func observeChangeInUserProperty() {
let ref = Database.database().reference().child("Users")
ref.observe(.childChanged, with: { snapshot in
let key = snapshot.key
let tag = snapshot.childSnapshot(forPath: "Tag").value as! String // ! = never optional
//get the user from the allUsersArray by its key
if let user = self.allUsersArray.first(where: { $0.Tag == key }) {
if user.Tag != tag { //if the tag changed, handle it
user.Tag = tag //update the allUsersArray
if tag == "Macro" { //if the new tag is Macro remove the user from the Micro array
if let userIndex = self.microUsersArray.firstIndex(where: { $0.Tag == key }) {
self.microUsersArray.remove(at: userIndex)
self.macroUsersArray.append(user) //add user to macro array
}
} else { //new type is micro so remove from macro array
if let userIndex = self.macroUsersArray.firstIndex(where: { $0.Tag == key }) {
self.macroUsersArray.remove(at: userIndex)
self.microUsersArray.append(user)
}
}
//reload the tableviews to reflect the changes
self.tableView.reloadData()
self.microTableView.reloadData()
}
}
})
}
}
Я так близок к решению этой проблемы. Любая помощь была бы замечательной.
Редактировать:
func loadAllUsersAndPopulateArray() {
let ref = Database.database().reference().child("Users").child("Talent")
ref.observeSingleEvent(of: .value, with: { snapshot in
let allUsersSnapshot = snapshot.children.allObjects as! [DataSnapshot]
for userSnap in allUsersSnapshot {
let user = User(from: userSnap)
self.allUsersArray.append(user!)
self.allUsersNames.append(user!.Name!)
}
self.observeChangeInUserProperty()
})
self.macroUsersArray = self.allUsersArray.filter { $0.Tag == "Macro" }
self.microUsersArray = self.allUsersArray.filter { $0.Tag == "Micro" }
self.tableView.reloadData()
self.microTableView.reloadData()
}
Комментарии:
1. У вас есть код в неправильном месте. При заполнении allUsersArray
for userSnap in allUsersSnapshot
вам не нужно продолжать вызыватьself.macroUsersArray
илиself.observeChange
повторять снова и снова для каждого пользователя. Сначала заполните allUsersArray, затем сразу после этого цикла заполните макро- и микромассивы и добавьте наблюдателя. И в качестве рекомендации по устранению неполадок временно исключите Firebase из уравнения и создайте некоторые фиктивные тестовые данные в качестве источника (ов) данных TableView, чтобы убедиться, что просмотры таблиц и ячейки действительно работают правильно.2. Я исправил код, но коды моих таблиц работают нормально, когда у меня есть другие данные. Я не понимаю, почему таблицы не заполняются.
3. Вам действительно нужно выполнить некоторые действия по устранению неполадок, чтобы понять, что не так. Добавьте точки останова и пройдитесь по своему коду — посмотрите
func tableView(_ tableView: UITableView, numberOfRowsInSection
, возвращает ли он правильное число строк. Затем проверьтеfunc tableView(_ tableView: UITableView, cellForRowAt
и посмотрите, заполняется ли ячейка допустимыми строками и т.д.4. Итак, я устранил некоторые проблемы, и за пределами функции loadAllUsers массив allusers не добавляется. Я проверил внутри моментального снимка, но вне функции массивы пусты
5. Вы должны обновить код в вопросе в
loadAllUsersAndPopulateArray
функции в соответствии с моим комментарием выше, поскольку он все еще неверен. Что касаетсяallUsersArray
того, что вы пусты вне этой функции, у вас на руках загадка. Он был бы пустым только в том случае, если ViewController вышел из области видимости, был вызван несколько раз (так что вы указываете на другой экземпляр, чем вы думали) или если вы удаляете содержимое. Я подозреваю, что это может иметь какое-то отношение к ViewController, поскольку вызов этогоvar allUsersArray = [User]()
создает пустой массив. Добавьте точки останова, пройдитесь по коду.
Ответ №1:
С помощью Джея я смог найти решение!
Мне нужно было настроить информацию о своих массивах изнутри закрытия. Всякий раз, когда обновляется тег пользователя, приложение необходимо закрыть и снова открыть, чтобы показать новые изменения, внесенные в firebase.
Спасибо @Jay за всю помощь!!!
вот новый код функции:
func loadAllUsersAndPopulateArray() {
let ref = Database.database().reference().child("Users")
ref.observeSingleEvent(of: .value, with: { snapshot in
let allUsersSnapshot = snapshot.children.allObjects as! [DataSnapshot]
for userSnap in allUsersSnapshot {
let user = User(from: userSnap)
self.allUsersArray.append(user!)
self.allUsersNames.append(user!.Name!)
self.macroUsersArray = self.allUsersArray.filter { $0.Tag == "Macro" }
self.microUsersArray = self.allUsersArray.filter { $0.Tag == "Micro" }
self.tableView.reloadData()
self.microTableView.reloadData()
}
self.observeChangeInUserProperty()
})
Комментарии:
1. Вы должны изменить это в соответствии с инструкциями, которые я предоставил в комментарии. Как есть, для каждого пользователя вы переназначаете и фильтруете массив снова и снова. Это много лишних потраченных впустую циклов процессора. Наряду с этим повторная перезагрузка TableView приведет к его мерцанию. Переместите это
self.macroUsersArray
иself.microUsersArray
вместе с перезагрузкой TableView после цикла for.
Ответ №2:
Проблема, с которой вы столкнулись, заключается в том, что Firebase является асинхронной — данные firebase действительны только в пределах замыкания, следующего за функцией Firebase.
Код после этого закрытия будет выполняться перед кодом внутри закрытия, поэтому вам нужно планировать обработку этих данных асинхронным способом.
Кроме того, поскольку вы заполняете массив данными из firebase и других массивов, основанных на первом, нет причин фильтровать их снова и снова; заполните основной массив, а затем, когда это будет сделано, заполните другие массивы.
Наконец, просмотр таблиц чувствителен, и их повторная загрузка может вызвать мерцание. Опять же, заполните свой основной массив, и как только это будет сделано, а другие массивы заполнены, перезагрузите tableViews — один раз.
Вот как должен выглядеть этот раздел кода
func loadAllUsersAndPopulateArray() {
let ref = Database.database().reference().child("Users")
ref.observeSingleEvent(of: .value, with: { snapshot in
let allUsersSnapshot = snapshot.children.allObjects as! [DataSnapshot]
for userSnap in allUsersSnapshot {
let user = User(from: userSnap)
self.allUsersArray.append(user!)
self.allUsersNames.append(user!.Name!)
}
self.macroUsersArray = self.allUsersArray.filter { $0.Tag == "Macro" }
self.microUsersArray = self.allUsersArray.filter { $0.Tag == "Micro" }
self.tableView.reloadData()
self.microTableView.reloadData()
self.observeChangeInUserProperty()
})
}
Я также предложу избавиться от allUsersNames
массива, поскольку эти имена также хранятся в allUsersArray
.
Комментарии:
1. к сожалению, когда я перемещаю filters и table.reloadData() за пределы того места, где они у меня есть, таблицы не заполняются, а массивы остаются пустыми. Когда я настраиваю его с помощью вашего кода, мои массивы остаются пустыми, и таблица не заполняется моими пользователями.
2. @NewCoder Тогда у вас что-то еще не так или это
(tableView === self.tableView)
вызывает проблему, поскольку в этом, вероятно, нет необходимости. На самом деле это не совпадает с вашими другими функциями делегирования, поэтому вы можете переосмыслить, как они кодируются.