fetchedResultsController 对象的表视图部分

Posted

技术标签:

【中文标题】fetchedResultsController 对象的表视图部分【英文标题】:Table View sections for fetchedResultsController objects 【发布时间】:2020-09-24 17:41:47 【问题描述】:

我正在尝试为我的 tableView 数据编制索引,并按姓名首字母对列出的人员的姓名进行排序。我已经看过很多关于使用 genericall 非持久数据的教程,但没有使用 Core Data 和使用 fetchedResultsController 选项(用于 iCloud 自动同步)。

现在我的代码如下(为了清楚起见,我只转录与问题相关的部分):

//The two arrays to populate from Core Data, used later to populate the table
var alumnosDictionaryArray = [String:[String]]()
var titulosSeccionArray = [String]()

override func viewDidLoad() 
    super.viewDidLoad()
    
    //Obtain data from Core Data    
    updateFetchedResultsController()

    //Populate the arrays with this data
    populateArrays()


func updateFetchedResultsController() 
    guard let context = container?.viewContext else  return 
    
    context.performAndWait 
        
        let sortDescriptor = NSSortDescriptor(key: "nombre", ascending: true)
        request.sortDescriptors = [sortDescriptor]
        fetchedResultsController = NSFetchedResultsController<Alumno>(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
        
        do 
            try fetchedResultsController?.performFetch()
            tableView.reloadData()
         catch 
            print("Error obtaining data: \(error)")
        
    


func populateArrays() 
    
    if let objects = fetchedResultsController?.fetchedObjects 
        
        for alumno in objects 
            let inicial = String(alumno.nombre?.prefix(1) ?? "")
            
            if var values = alumnosDictionaryArray[inicial] 
                values.append(alumno.nombre!)
                alumnosDictionaryArray[inicial] = values
             else 
                alumnosDictionaryArray[inicial] = [alumno.nombre!]
            
        
        
        titulosSeccionArray = [String](alumnosDictionaryArray.keys)
        titulosSeccionArray = titulosSeccionArray.sorted(by: $0 < $1)
    

这部分似乎工作正常,因为数组已正确填充,正如我已检查打印语句一样。

稍后,对于表格数据:

override func numberOfSections(in tableView: UITableView) -> Int 
    
    return titulosSeccionArray.count


override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 

    let inicial = titulosSeccionArray[section]
    if let values = alumnosDictionaryArray[inicial] 
        return values.count
    
    return 0


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! AlumnoCell
    
    if self.validateIndexPath(indexPath) 
        
        let object = fetchedResultsController?.object(at: indexPath)
        let inicial = titulosSeccionArray[indexPath.section]
       
        if let values = alumnosDictionaryArray[inicial] 
            
            cell.nombreField.text = values[indexPath.section]
            cell.especialidadField.text = object?.especialidadRelacionada?.nombre
            cell.cursoField.text = object?.cursoRelacionado?.nivel
            cell.tutorField.text = object?.tutorRelacionado?.nombre
        
     else 
        print("Error from indexPath")
    
  
    return cell


//To validate indexPath
func validateIndexPath(_ indexPath: IndexPath) -> Bool 
    if let sections = self.fetchedResultsController?.sections,
       indexPath.section < sections.count 
        if indexPath.row < sections[indexPath.section].numberOfObjects 
            return true
        
    
    return false


override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 
    
    return titulosSeccionArray[section]

我得到了一个或多或少完整的表格,其中包含正确的部分、部分的标题(首字母)以及每个部分的行数,但似乎只有第一行有效,因为其余数据为空并抛出选择任何时出错:“索引 1 处没有部分”。事实上,在抛出异常之前,我在控制台上收到了许多“来自 indexPath 的错误”消息......

有什么建议吗?谢谢。

【问题讨论】:

我相信如果您使用 NSFetchedResultsController 内置的部分功能而不是尝试使用您自己的数组来做这件事,这会更容易。完成此操作后,我将“部分”字段放入对象中,然后在获取时将其用作部分键路径。这样,表格视图可以直接从结果中填充。 谢谢。你能不能给我一个更详细的例子。我从swift开始,并不清楚你的想法。 【参考方案1】:

对于 Alumno 实体,再添加 1 列 nombreFirstChar。 然后使用类似这样的东西将这个变量保存在数据库中:

alumno.nombreFirstChar = nombre.firstCharacter()

这里firstCharacter的辅助方法会是这样的:

func firstCharacter() -> String 
    var firstCharacter = self.first ?? "#"
    
    if (!(firstCharacter >= "a" && firstCharacter <= "z") && !(firstCharacter >= "A" && firstCharacter <= "Z") ) 
        firstCharacter = "#"
    
    return String(firstCharacter).capitalized

现在在您的 fetchedResultsController 中,替换这一行

fetchedResultsController = NSFetchedResultsController<Alumno>(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)      

fetchRequest.sortDescriptors = [NSSortDescriptor(key: "nombreFirstChar", ascending: true)]
fetchRequest.propertiesToGroupBy = ["nombreFirstChar"]
fetchedResultsController = NSFetchedResultsController<Alumno>(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nombreFirstChar, cacheName: nil)

然后在table view delegate apis中,你可以像这样使用fetchedResultsController:

func numberOfSections(in tableView: UITableView) -> Int 
  return fetchedResultsController.sections?.count ?? 0


func sectionIndexTitles(for tableView: UITableView) -> [String]? 
    
    guard let fetchedResultsSections: [NSFetchedResultsSectionInfo] = channelFetchedResultsController.sections else return nil
    
    return fetchedResultsSections!.mapString($0.name)


func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 
    guard var fetchedResultsSections: [NSFetchedResultsSectionInfo] = fetchedResultsController.sections else return nil
    return fetchedResultsSections[section].name
    


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 

    guard var sections:[NSFetchedResultsSectionInfo] = fetchedResultsController.sections, let section = sections[section] else 
        return 0
    
    
    return section.numberOfObjects


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
    cell.accessoryType = .disclosureIndicator
    let alumno = fetchedResultsController.object(at: indexPath)
    ...
    return cell

【讨论】:

以上是关于fetchedResultsController 对象的表视图部分的主要内容,如果未能解决你的问题,请参考以下文章

FetchedResultsController 中没有部分

如何将对象从 fetchedResultsController 到 Plist?

fetchedResultsController 对象的表视图部分

fetchedResultsController 和 Integer

将 fetchedResultsController 更改为 protected from private

将 fetchedResultsController 与 swift3 一起使用