#swift #uitableview #google-cloud-firestore #swift5
# #swift #UITableView #google-облако-firestore #swift5
Вопрос:
Я пытаюсь извлечь данные изображения из Firestore и обновить пользовательский интерфейс, добавив изображения в ячейки TableView.
Тем не менее, я получаю ошибку выхода индекса за пределы диапазона. Я считаю, что это из-за проблемы с синхронизацией.
Можете ли вы, пожалуйста, помочь мне это исправить?
Заранее спасибо.
Неустранимая ошибка: индекс вне диапазона: файл Swift / ContiguousArrayBuffer.swift, строка 444
override func viewDidLoad() {
super.viewDidLoad()
createBubbleView()
getDataFromFireStore()
detailsTableView.delegate = self
detailsTableView.dataSource = self
// MARK: - NIB REGISTRATION
detailsTableView.register(UINib(nibName: K.ingredientsNibName, bundle: nil), forCellReuseIdentifier: K.ingredientsCell)
detailsTableView.register(UINib(nibName: K.stepsNibName, bundle: nil), forCellReuseIdentifier: K.stepsCell)
detailsTableView.register(UINib(nibName: K.recipeNameNib, bundle: nil), forCellReuseIdentifier: K.recipeNameCell) }
func getDataFromFireStore() {
let db = Firestore.firestore()
for ingredient in ingredients {
db.collection("Ingredients").whereField("name", isEqualTo: ingredient)
.getDocuments() { [self] (querySnapshot, err) in
if let err = err {
print("Error getting documents: (err)")
} else {
for document in querySnapshot!.documents {
print(document.get("image")!)
if let imageURLsDB = document.get("image") as? [String] {
ingredientsImageURLs.append(document.get("image") as! String)
}
}
}
}
}
// MARK: - RETURNS THE VALUE OF EACH CELL IN THE TABLEVIEW
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = detailsTableView.dequeueReusableCell(withIdentifier: K.ingredientsCell)
switch indexPath.section {
// RECIPE NAME
case 0:
let cell = detailsTableView.dequeueReusableCell(withIdentifier: K.recipeNameCell) as! RecipeTitleCell
cell.cookingTimeLabel.text = cookingTime
cell.numberOfIngredientsLabel.text = String(ingredients.count)
return cell
// INGREDIENTS
case 1:
let cell = detailsTableView.dequeueReusableCell(withIdentifier: K.ingredientsCell) as! IngredientsCell
cell.ingredientName.text = ingredients[indexPath.row]
cell.textLabel?.font = UIFont(name:"Mulish-regular", size:12)
cell.textLabel?.numberOfLines = 0
cell.textLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping
DispatchQueue.main.async {
cell.ingredientImage.sd_setImage(with: URL(string: self.ingredientsImageURLs[indexPath.row]), placeholderImage: UIImage(systemName: "square.and.arrow.up"))
self.detailsTableView.reloadData()
}
return cell
Комментарии:
1. В какой строке вы получаете ошибку? Как выглядят методы подсчета строк и разделов и как
ingredients
ingredientsImageURLs
синхронизируются массивы and?2. @JoakimDanielson, я получаю сообщение об ошибке при попытке отобразить изображения в ячейке ingredientImage. Вы можете увидеть строку ниже; cell.ingredientImage.sd_setImage(с: URL(строка: self.ingredientsImageURLs[indexPath.row]), placeholderImage: UIImage(имясисТемы: «square.and.arrow.up»))
3. Тогда, возможно, вам также следует ответить на другие мои вопросы.
4. @JoakimDanielson, извините. Пожалуйста, посмотрите скриншот здесь — drive.google.com/file/d/1q3md__tZ7tONUsH_e_H6G63SRFUXZQjw /… ingredients и ingredientsImageURLs содержат одинаковое количество элементов в firestore. Однако при отладке я вижу 0 элементов в ingredientsImageURLs
5. Я предполагаю, что код, в котором происходит сбой, вызывается до того, как некоторые (или все) элементы в ingredientsImageURLs были загружены в функцию getDataFromFireStore . Вероятно, лучший способ избежать этого — использовать один массив вместо двух.
Ответ №1:
Спасибо, ребята, за информацию. Вот как я это исправил.
- Установите изображения в ячейки после получения нужного количества данных
if ingredientsImageURLs.count == ingredients.count { cell.ingredientImage.sd_setImage(with: URL(string: self.ingredientsImageURLs[indexPath.row]), placeholderImage: UIImage(systemName: "square.and.arrow.up")) }
- Перезагрузите данные после загрузки всех изображений.
Функция getDataFromFireStore() {
let db = Firestore.firestore() let recipeDocRef = db.collection("Recipes").document(selectedRecipeID).addSnapshotListener { [self] (snapshot, error) in if let titleDB = snapshot?.get("title") as? String { self.recipeTitle = titleDB titleLabel.text = recipeTitle } if let ingredientsDB = snapshot?.get("ingredients") as? [String] { self.ingredients = ingredientsDB } if let cookingTimeDB = snapshot?.get("cookingTime") as? String { self.cookingTime = cookingTimeDB } if let imageUrlDB = snapshot?.get("imageUrl") as? String { self.imageUrl = imageUrlDB recipeImageView.sd_setImage(with: URL(string: imageUrl), placeholderImage: UIImage(systemName: "square.and.arrow.up")) } if let cookingStepsDB = snapshot?.get("cookingSteps") as? [String] { self.steps = cookingStepsDB } if let stepImagesDB = snapshot?.get("stepImages") as? [String]{ self.stepsImages = stepImagesDB } for ingredient in self.ingredients { db.collection("Ingredients").whereField("name", isEqualTo: ingredient) .getDocuments() { [self] (querySnapshot, err) in if let err = err { print("Error getting documents: (err)") } else { for document in querySnapshot!.documents { ingredientsImageURLs.append(document.get("image") as! String) } self.detailsTableView.reloadData() } } self.detailsTableView.reloadData() } }
}
Ответ №2:
if let imageURLsDB = document.get("image") as? [String] {
ingredientsImageURLs.append(document.get("image") as! String)
}
Вы проверяете document.get(«image») как строковый массив, но при добавлении в свой массив вы проверяете как строку.
Комментарии:
1. Обновил его, но, к сожалению, это не решило проблему.
2. Когда вы добавляете данные в ингредиенты?
3. Это уже в БД. Он просто извлекает эти данные.
4. Пожалуйста, можете ли вы поделиться со мной всем кодом ViewController? Вы можете добавить на диск.
Ответ №3:
Вы вызываете серверную часть для изображения, а затем повторно загружаете данные, поэтому он вызывает каждый раз, устанавливая изображение без остановки. Вы не можете вызвать reload data внутри cellForRow, потому что это приведет к повторному вызову делегатов табличного представления.
private func getImages()
DispatchQueue.main.async {
cell.ingredientImage.sd_setImage(with: URL(string:self.ingredientsImageURLs[indexPath.row]), placeholderImage: UIImage(systemName: "square.and.arrow.up"))
self.imageArray = imagesFromFireStore
self.detailsTableView.reloadData()
}
// вы можете поместить свой код здесь
if let imageURLsDB = document.get("image") as? [String] {
ingredientsImageURLs.append(document.get("image") as! String)
// here just append images to array and call
self.reloadData()
}
В табличном представлении используйте свой imageArray[indexPath.row]
Комментарии:
1. Вы правы. Я обновил этот раздел. Однако TableView cellForRow все еще выполняется перед извлечением данных из Firestore. Как я могу заполнить изображения после того, как у меня будут готовы данные из Firestore?
2. создайте пустой массив изображений в контроллере, затем создайте функцию, которая вызывает изображения, заполняет массив, когда к вам поступают данные, и в этом вызове функции перезагружает данные. Я только что отредактировал ответ, конечно, вы не будете вызывать его с помощью строки пути к индексу, использовать для каждого или чего-то еще.