Здравствуйте, при обновлении PopularTableViewController столкнулся с ошибкой : Index out of range…
Подскажите как решить,
Курс #2 : PopularTableViewController : fatal error: Index out of range
Добрый вечер!
Распечатайте значения indexPath.row и посмотрите сколько Вы ожидаете. А так да, у Вас что-то с индексами. Раз у Вас выходит ошибка, то Вы получаете индекс, которого нет в массиве. Например, у Вас массив с 3 элементами, а Вы обращаетесь к 6.
Весь код с курса, ничего не добавлял и не исправлял…
Может это зависит от количества добавляемых ресторанов в NewEateryTableViewController и последующей синхронизацией с iCloud?
У меня та же ошибка.
Обнаружил причину ошибки. Если в методе, который загружает данные из iCloud обнулить массив (cloudPlaces = [ ] ) чтобы данные не дублировались - то возникает ошибка. Если этого не делать, то ошибки не будет. Но как же тогда избавиться от дублирования записей?
А покажите Ваш метод
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ???????
}
Значит сырые данные из клауда, вы их не извлекаете?
Создаете екстеншн
extension Array {
func filterDuplicates (includeElement: (_ lhs:Element, _ rhs:Element) -> Bool) -> [Element] {
var results = [Element]()
forEach { element in
let existingElements = results.filter {
return includeElement(element, $0)
}
if existingElements.count == 0 {
results.append(element)
}
}
return results
}
}
используете его
let uniqueElementsArray = cloudPlaces.filterDuplicates { $0.recordID != $1.recordID }
К сожалению не сработало
Printing description of indexPath:
expression produced error: error: /var/folders/wl/wfjhbw6d0dv4xspj2qn5t1xc0000gn/T/./lldb/986/expr7.swift:1:65: error: use of undeclared type 'Foundation’
Swift._DebuggerSupport.stringForPrintObject(Swift.UnsafePointer<Foundation.IndexPath>(bitPattern: 0x102078700)!.pointee)
^~~~~~~~~~
Printing description of indexPath._indexes:
▿ 2 elements
- 0 : 0
- 1 : 4
(lldb)
Я еще новичок, поэтом не на все вопросы могу адекватно ответить. Для лучшего понимания дублирую весь мой код:
import UIKit
import CoreData
import CloudKit
class VeggiePlaceCommunityPlacesTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {
var cloudPlaces: [CKRecord] = []
var spinner: UIActivityIndicatorView!
var publicDataBase = CKContainer.default().publicCloudDatabase
@IBAction func close(segue: UIStoryboardSegue) {
}
override func viewWillAppear(_ animated: Bool) {
navigationController?.hidesBarsOnSwipe = true
}
override func viewDidLoad() {
super.viewDidLoad()
refreshControl = UIRefreshControl()
refreshControl?.backgroundColor = .white
refreshControl?.tintColor = #colorLiteral(red: 0.3586771858, green: 0.8259628539, blue: 0.7560831902, alpha: 1)
refreshControl?.addTarget(self, action: #selector(getCloudRecords), for: .valueChanged)
spinner = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
spinner.color = #colorLiteral(red: 0.3586771858, green: 0.8259628539, blue: 0.7560831902, alpha: 1)
spinner.translatesAutoresizingMaskIntoConstraints = false
spinner.hidesWhenStopped = true
spinner.startAnimating()
tableView.addSubview(spinner)
NSLayoutConstraint(item: spinner, attribute: .centerX, relatedBy: .equal, toItem: tableView, attribute: .centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: spinner, attribute: .centerY, relatedBy: .equal, toItem: tableView, attribute: .centerY, multiplier: 0.85, constant: 0).isActive = true
getCloudRecords()
tableView.estimatedRowHeight = 85
tableView.rowHeight = UITableViewAutomaticDimension
tableView.separatorColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
func getCloudRecords() {
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "Restaurant", predicate: predicate)
let sort = NSSortDescriptor(key: "creationDate", ascending: false)
query.sortDescriptors = [sort]
let queryOperation = CKQueryOperation(query: query)
queryOperation.desiredKeys = ["name","type","location","phone","image", "webSite"]
// queryOperation.resultsLimit = 10
queryOperation.queuePriority = .veryHigh
queryOperation.recordFetchedBlock = { (record: CKRecord!) in
if let record = record {
self.cloudPlaces.append(record)
}
}
queryOperation.queryCompletionBlock = { (cursor, error) in
guard error == nil else {
print("Не удалось получить записи из iCloud: \(String(describing: error?.localizedDescription))")
return
}
print("Записи успешно получены из iCloud")
DispatchQueue.main.async {
self.tableView.reloadData()
self.spinner.stopAnimating()
self.refreshControl?.endRefreshing()
}
}
publicDataBase.add(queryOperation)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let userDefaults = UserDefaults.standard
let wasIntroWatched = userDefaults.bool(forKey: "wasIntroWatched")
guard !wasIntroWatched else { return }
if let pageViewController = storyboard?.instantiateViewController(withIdentifier: "pageViewController") as? PageViewController {
present(pageViewController, animated: true, completion: nil)
}
}
// MARK: - fetch results controller delegate
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cloudPlaces.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! VeggiePlaceCommunityPlacesTableViewCell
let cloudPlace = cloudPlaces[indexPath.row]
cell.thumbnailImageView?.image = UIImage(named: "grapes")
if let image = cloudPlace.object(forKey: "image") {
let image = image as! CKAsset
let data = try? Data(contentsOf: image.fileURL)
if let data = data {
cell.thumbnailImageView?.image = UIImage(data: data)
}
}
cell.thumbnailImageView.layer.cornerRadius = 32.5
cell.thumbnailImageView.clipsToBounds = true
cell.nameLabel.text = cloudPlace.object(forKey: "name") as? String
if let cloudLocation = cloudPlace.object(forKey: "location") as? String {
if cloudLocation != "" {
cell.locationLabel.text = cloudLocation
} else if let cloudWebSite = cloudPlace.object(forKey: "webSite") as? String {
if cloudWebSite != "" {
cell.locationLabel.text = cloudWebSite
} else { cell.locationLabel.text = "Адрес не указан" }
}
}
cell.typeLabel.text = cloudPlace.object(forKey: "type") as? String
return cell
}
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let share = UITableViewRowAction(style: .default, title: "Поделиться") { (action, indexPath) in
let cloudPlace = self.cloudPlaces[indexPath.row]
let shareName = cloudPlace.object(forKey: "name") as! String
let shareLocation = cloudPlace.object(forKey: "location") as! String
var shareImage: UIImage!
let defaultText = "Я сейчас в " + shareName + " По адресу: " + shareLocation
if let image = cloudPlace.object(forKey: "image") {
let image = image as! CKAsset
let data = try? Data(contentsOf: image.fileURL)
if let data = data {
shareImage = UIImage(data: data)
}
}
let activityController = UIActivityViewController(activityItems: [shareImage, defaultText], applicationActivities: nil)
self.present(activityController, animated: true, completion: nil)
}
share.backgroundColor = #colorLiteral(red: 0.3490196078, green: 0.8, blue: 0.7254901961, alpha: 1)
return[share]
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "detailSegue" {
if let indexPath = tableView.indexPathForSelectedRow {
let dvc = segue.destination as! CommunityPlaceDetailViewController
let cloudPlace = cloudPlaces[indexPath.row]
dvc.cloudName = cloudPlace.object(forKey: "name") as! String
if let location = cloudPlace.object(forKey: "location") {
dvc.cloudLocation = location as! String
if dvc.cloudLocation == "" {
dvc.cloudLocation = "Адрес не указан"
}
} else {
dvc.cloudLocation = "Адрес не указан"
}
dvc.cloudType = cloudPlace.object(forKey: "type") as! String
dvc.cloudPhone = cloudPlace.object(forKey: "phone") as! String
if let webSite = cloudPlace.object(forKey: "webSite") {
dvc.cloudWebSite = webSite as! String
if dvc.cloudWebSite == "" {
dvc.cloudWebSite = "Сайт не указан"
}
} else {
dvc.cloudWebSite = "Сайт не указан"
}
if let image = cloudPlace.object(forKey: "image") {
let image = image as! CKAsset
let data = try? Data(contentsOf: image.fileURL)
if let data = data {
dvc.cloudImage = UIImage(data: data)
}
}
}
}
}
}
import UIKit
import CoreData
import CloudKit
class VeggiePlaceCommunityPlacesTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {
var cloudPlaces: [CKRecord] = []
var spinner: UIActivityIndicatorView!
var publicDataBase = CKContainer.default().publicCloudDatabase
@IBAction func close(segue: UIStoryboardSegue) {
}
override func viewWillAppear(_ animated: Bool) {
navigationController?.hidesBarsOnSwipe = true
}
override func viewDidLoad() {
super.viewDidLoad()
refreshControl = UIRefreshControl()
refreshControl?.backgroundColor = .white
refreshControl?.tintColor = #colorLiteral(red: 0.3586771858, green: 0.8259628539, blue: 0.7560831902, alpha: 1)
refreshControl?.addTarget(self, action: #selector(getCloudRecords), for: .valueChanged)
spinner = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
spinner.color = #colorLiteral(red: 0.3586771858, green: 0.8259628539, blue: 0.7560831902, alpha: 1)
spinner.translatesAutoresizingMaskIntoConstraints = false
spinner.hidesWhenStopped = true
spinner.startAnimating()
tableView.addSubview(spinner)
NSLayoutConstraint(item: spinner, attribute: .centerX, relatedBy: .equal, toItem: tableView, attribute: .centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: spinner, attribute: .centerY, relatedBy: .equal, toItem: tableView, attribute: .centerY, multiplier: 0.85, constant: 0).isActive = true
getCloudRecords()
tableView.estimatedRowHeight = 85
tableView.rowHeight = UITableViewAutomaticDimension
tableView.separatorColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
func getCloudRecords() {
let uniqueElementsArray = cloudPlaces.filterDuplicates { $0.recordID != $1.recordID }
cloudPlaces = uniqueElementsArray
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "Restaurant", predicate: predicate)
let sort = NSSortDescriptor(key: "creationDate", ascending: false)
query.sortDescriptors = [sort]
let queryOperation = CKQueryOperation(query: query)
queryOperation.desiredKeys = ["name","type","location","phone","image", "webSite"]
// queryOperation.resultsLimit = 10
queryOperation.queuePriority = .veryHigh
queryOperation.recordFetchedBlock = { (record: CKRecord!) in
if let record = record {
self.cloudPlaces.append(record)
}
}
queryOperation.queryCompletionBlock = { (cursor, error) in
guard error == nil else {
print("Не удалось получить записи из iCloud: \(String(describing: error?.localizedDescription))")
return
}
print("Записи успешно получены из iCloud")
DispatchQueue.main.async {
self.tableView.reloadData()
self.spinner.stopAnimating()
self.refreshControl?.endRefreshing()
}
}
publicDataBase.add(queryOperation)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let userDefaults = UserDefaults.standard
let wasIntroWatched = userDefaults.bool(forKey: "wasIntroWatched")
guard !wasIntroWatched else { return }
if let pageViewController = storyboard?.instantiateViewController(withIdentifier: "pageViewController") as? PageViewController {
present(pageViewController, animated: true, completion: nil)
}
}
// MARK: - fetch results controller delegate
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cloudPlaces.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! VeggiePlaceCommunityPlacesTableViewCell
let cloudPlace = cloudPlaces[indexPath.row]
cell.thumbnailImageView?.image = UIImage(named: "grapes")
if let image = cloudPlace.object(forKey: "image") {
let image = image as! CKAsset
let data = try? Data(contentsOf: image.fileURL)
if let data = data {
cell.thumbnailImageView?.image = UIImage(data: data)
}
}
cell.thumbnailImageView.layer.cornerRadius = 32.5
cell.thumbnailImageView.clipsToBounds = true
cell.nameLabel.text = cloudPlace.object(forKey: "name") as? String
if let cloudLocation = cloudPlace.object(forKey: "location") as? String {
if cloudLocation != "" {
cell.locationLabel.text = cloudLocation
} else if let cloudWebSite = cloudPlace.object(forKey: "webSite") as? String {
if cloudWebSite != "" {
cell.locationLabel.text = cloudWebSite
} else { cell.locationLabel.text = "Адрес не указан" }
}
}
cell.typeLabel.text = cloudPlace.object(forKey: "type") as? String
return cell
}
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let share = UITableViewRowAction(style: .default, title: "Поделиться") { (action, indexPath) in
let cloudPlace = self.cloudPlaces[indexPath.row]
let shareName = cloudPlace.object(forKey: "name") as! String
let shareLocation = cloudPlace.object(forKey: "location") as! String
var shareImage: UIImage!
let defaultText = "Я сейчас в " + shareName + " По адресу: " + shareLocation
if let image = cloudPlace.object(forKey: "image") {
let image = image as! CKAsset
let data = try? Data(contentsOf: image.fileURL)
if let data = data {
shareImage = UIImage(data: data)
}
}
let activityController = UIActivityViewController(activityItems: [shareImage, defaultText], applicationActivities: nil)
self.present(activityController, animated: true, completion: nil)
}
share.backgroundColor = #colorLiteral(red: 0.3490196078, green: 0.8, blue: 0.7254901961, alpha: 1)
return[share]
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "detailSegue" {
if let indexPath = tableView.indexPathForSelectedRow {
let dvc = segue.destination as! CommunityPlaceDetailViewController
let cloudPlace = cloudPlaces[indexPath.row]
dvc.cloudName = cloudPlace.object(forKey: "name") as! String
if let location = cloudPlace.object(forKey: "location") {
dvc.cloudLocation = location as! String
if dvc.cloudLocation == "" {
dvc.cloudLocation = "Адрес не указан"
}
} else {
dvc.cloudLocation = "Адрес не указан"
}
dvc.cloudType = cloudPlace.object(forKey: "type") as! String
dvc.cloudPhone = cloudPlace.object(forKey: "phone") as! String
if let webSite = cloudPlace.object(forKey: "webSite") {
dvc.cloudWebSite = webSite as! String
if dvc.cloudWebSite == "" {
dvc.cloudWebSite = "Сайт не указан"
}
} else {
dvc.cloudWebSite = "Сайт не указан"
}
if let image = cloudPlace.object(forKey: "image") {
let image = image as! CKAsset
let data = try? Data(contentsOf: image.fileURL)
if let data = data {
dvc.cloudImage = UIImage(data: data)
}
}
}
}
}
}
extension Array {
func filterDuplicates (includeElement: (_ lhs:Element, _ rhs:Element) -> Bool) -> [Element] {
var results = Element
forEach { element in
let existingElements = results.filter {
return includeElement(element, $0)
}
if existingElements.count == 0 {
results.append(element)
}
}
return results
}
}
Это нужно делать после того, как вы получили данные
let uniqueElementsArray = cloudPlaces.filterDuplicates { $0.recordID != $1.recordID }
cloudPlaces = uniqueElementsArray
И можно заменить на
cloudPlaces = cloudPlaces.filterDuplicates { $0.recordID != $1.recordID }