如何使用CoreData结果快速在UITableview的正确部分中排序和显示 [英] How can I sort and show in correct section of a UITableview in swift using CoreData results

查看:70
本文介绍了如何使用CoreData结果快速在UITableview的正确部分中排序和显示的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题的最底部是我的库存控制器文件.我的问题是我在所有部分中得到重复的结果.我将原因缩小到

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

我在该函数中的代码未考虑每个部分中有多少行.因此,我只是在每个部分中打印出相同的重复结果.

实际问题在下面的图像之后列出 ...

请参考以下图片:

我还可以从设置菜单中更改索引,以便它可以按数字索引,例如0-9.请参考下图:

也就是说,我目前正在从Core Data加载数据.随附的是我使用的实体及其相关关系的参考图像.

问题:

我的问题是,如何从coreData中获取结果以将其分类为A,B,C类型部分或1,2,3部分,从而使表的浏览变得简单.

我的直觉是说let inventoryRecords = try moc.executeFetchRequest(inventoryFetchRequest) as? [Inventory]需要一个排序描述符根据我的喜好进行排序,但是然后我如何获取数据并放入正确的数组结构中以拆分成我需要的部分...我不知道.

globals.swift

import Foundation
import CoreData

//Array of Inventory & Store Core Data Managed Objects
var g_inventoryItems = [Inventory]()
var g_storeList = [Store]()
var g_appSettings = [AppSettings]()
var g_demoMode = false

InventoryController.swift

import UIKit
import CoreData

class InventoryController: UIViewController, UISearchBarDelegate, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet weak var searchBar: UISearchBar!
    @IBOutlet weak var inventoryTable: UITableView!

    var numberIndex = ["0","1","2","3","4","5","6","7","8","9"]
    var letterIndex = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]

    var moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext //convinience variable to access managed object context

    // Start DEMO Related Code
    func createInventoryDummyData(number: Int) -> Inventory{
        let tempInventory = NSEntityDescription.insertNewObjectForEntityForName("Inventory", inManagedObjectContext: moc) as! Inventory

        tempInventory.name = "Test Item # \(number)"
        tempInventory.barcode = "00000000\(number)"
        tempInventory.currentCount = 0
        tempInventory.id = number
        tempInventory.imageLargePath = "http://website.tech//uploads/inventory/7d3fe5bfad38a3545e80c73c1453e380.png"
        tempInventory.imageSmallPath = "http://website.tech//uploads/inventory/7d3fe5bfad38a3545e80c73c1453e380.png"
        tempInventory.addCount = 0
        tempInventory.negativeCount = 0
        tempInventory.newCount = 0
        tempInventory.store_id = 1 //belongs to same store for now

        //Select a random store to belong to 0 through 2 since array starts at 0
        let aRandomInt = Int.random(0...2)
        tempInventory.setValue(g_storeList[aRandomInt], forKey: "store") //assigns inventory to one of the stores we created.

        return tempInventory
    }

    func createStoreDummyData(number:Int) -> Store{
        let tempStore = NSEntityDescription.insertNewObjectForEntityForName("Store", inManagedObjectContext: moc) as! Store

        tempStore.address = "100\(number) lane, Miami, FL"
        tempStore.email = "store\(number)@centraltire.com"
        tempStore.id = number
        tempStore.lat = 1.00000007
        tempStore.lng = 1.00000008
        tempStore.name = "Store #\(number)"
        tempStore.phone = "123000000\(number)"

        return tempStore
    }

    // End DEMO Related Code

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.
        print("InventoryController -> ViewDidLoad -> ... starting inits")

        //First check to see if we have entities already.  There MUST be entities, even if its DEMO data.
        let inventoryFetchRequest = NSFetchRequest(entityName: "Inventory")
        let storeFetchRequest = NSFetchRequest(entityName: "Store")

        do {
            let storeRecords = try moc.executeFetchRequest(storeFetchRequest) as? [Store]
            if(storeRecords!.count<=0){
                g_demoMode = true
                print("No store entities found.  Demo mode = True.  Creating default store entities...")

                var store : Store //define variable as Store type

                for index in 1...3 {
                    store = createStoreDummyData(index)
                    g_storeList.append(store)
                }
            }

            let inventoryRecords = try moc.executeFetchRequest(inventoryFetchRequest) as? [Inventory]
            if(inventoryRecords!.count<=0){
                g_demoMode = true
                print("No entities found for inventory.  Demo mode = True.  Creating default entities...")

                var entity : Inventory //define variable as Inventory type

                for index in 1...20 {
                    entity = createInventoryDummyData(index)
                    g_inventoryItems.append(entity)
                }

                print("finished creating entities")
            }
        }catch{
            fatalError("bad things happened \(error)")
        }

        print("InventoryController -> viewDidload -> ... finished inits!")
    }

    override func viewWillAppear(animated: Bool) {
        print("view appearing")
        //When the view appears its important that the table is updated.

        //Look at the selected Store & Use the LIST of Inventory Under it.

        inventoryTable.reloadData()//this is important to update correctly for changes that might have been made
    }

    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        // Get the new view controller using segue.destinationViewController.
        // Pass the selected object to the new view controller.
        print("inventoryItemControllerPrepareForSegueCalled")

        if segue.identifier == "inventoryInfoSegue" {
            let vc = segue.destinationViewController as! InventoryItemController
            if let cell = sender as? InventoryTableViewCell{
                vc.inventoryItem = cell.inventoryItem! //sets the inventory item accordingly, passing its reference along.
            }else{
                print("sender was something else")
            }
        }

    }

    func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
        //This scrolls to correct section based on title of what was pressed.
        return letterIndex.indexOf(title)!
    }

    func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
        //Use correct index on the side based on settings desired.
        if(g_appSettings[0].indextype=="letter"){
            return letterIndex
        }else{
            return numberIndex
        }
    }

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

        //TODO: Need to figure out how many rows for ...column A,B,C or 1,2,3 based on indexType using~
        //To do this we need to organize the inventory results into a section'ed array.

        if(g_appSettings[0].selectedStore != nil){
            return (g_appSettings[0].selectedStore?.inventories!.count)! //number of rows is equal to the selected stores inventories count
        }else{
            return g_inventoryItems.count
        }
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("InventoryTableCell", forIndexPath: indexPath) as! InventoryTableViewCell

        if(g_appSettings[0].selectedStore != nil){
            //Get the current Inventory Item & Set to the cell for reference.
            cell.inventoryItem = g_appSettings[0].selectedStore?.inventories?.allObjects[indexPath.row] as! Inventory
        }else{
            //This only happens for DEMO mode or first time.
            cell.inventoryItem = g_inventoryItems[indexPath.row]//create reference to particular inventoryItem this represents.
        }

        cell.drawCell() //uses passed inventoryItem to draw it's self accordingly.

        return cell

    }

    func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        if(g_appSettings[0].indextype == "letter"){
            return letterIndex[section]
        }else{
            return numberIndex[section]
        }

    }

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        if(g_appSettings[0].selectedStore != nil){
            if(g_appSettings[0].indextype=="letter"){
                return letterIndex.count
            }else{
                return numberIndex.count
            }
        }else{
            return 1//only one section for DEMO mode.
        }
    }

    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        //dispatch_async(dispatch_get_main_queue()) {
            //[unowned self] in
            print("didSelectRowAtIndexPath")//does not recognize first time pressed item for some reason?
            let selectedCell = self.tableView(tableView, cellForRowAtIndexPath: indexPath) as? InventoryTableViewCell
            self.performSegueWithIdentifier("inventoryInfoSegue", sender: selectedCell)
        //}

    }


    @IBAction func BarcodeScanBarItemAction(sender: UIBarButtonItem) {
        print("test of baritem")
    }
    @IBAction func SetStoreBarItemAction(sender: UIBarButtonItem) {
        print("change store interface")
    }

    func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
        print("text is changing")
    }

    func searchBarCancelButtonClicked(searchBar: UISearchBar) {
        print("ended by cancel")
        searchBar.text = ""
        searchBar.resignFirstResponder()
    }

    func searchBarSearchButtonClicked(searchBar: UISearchBar) {
        print("ended by search")
        searchBar.resignFirstResponder()
    }

    func searchBarTextDidEndEditing(searchBar: UISearchBar) {
        print("ended by end editing")
        searchBar.resignFirstResponder()
    }

    @IBAction func unwindBackToInventory(segue: UIStoryboardSegue) {
        print("unwind attempt")

        let barcode = (segue.sourceViewController as? ScannerViewController)?.barcode
        searchBar.text = barcode!

        print("barcode="+barcode!)

        inventoryTable.reloadData()//reload the data to be safe.

    }

}

//Extention to INT to create random number in range.
extension Int
{
    static func random(range: Range<Int> ) -> Int
    {
        var offset = 0

        if range.startIndex < 0   // allow negative ranges
        {
            offset = abs(range.startIndex)
        }

        let mini = UInt32(range.startIndex + offset)
        let maxi = UInt32(range.endIndex   + offset)

        return Int(mini + arc4random_uniform(maxi - mini)) - offset
    }
}

更新:: **

所以我环顾四周,发现了这篇文章(我实现了它).

https://www. andrewcbancroft.com/2015/03/05/displaying-data-with-nsfetchedresultscontroller-and-swift/

我现在真的很想找出答案.唯一的问题是我可以自动创建节,但是只能在另一个字段上,例如store.name,我无法将其节分为A,B,C节或1,2,3./p>

这是我使用该文章中所述方法的fetchedResultsController代码.

//Create fetchedResultsController to handle Inventory Core Data Operations
    lazy var fetchedResultsController: NSFetchedResultsController = {
        let inventoryFetchRequest = NSFetchRequest(entityName: "Inventory")
        let primarySortDescriptor = NSSortDescriptor(key: "name", ascending: true)
        let secondarySortDescriptor = NSSortDescriptor(key: "barcode", ascending: true)
        inventoryFetchRequest.sortDescriptors = [primarySortDescriptor, secondarySortDescriptor]

        let frc = NSFetchedResultsController(
            fetchRequest: inventoryFetchRequest,
            managedObjectContext: self.moc,
            sectionNameKeyPath: "store.name",
            cacheName: nil)

        frc.delegate = self

        return frc
    }()

问题是要为sectionNameKeyPath放置什么:现在将使其在A B C上进行划分,我明白了!

找到了一个与我的问题非常相似的stackoverflow帖子,但需要迅速回答.

来自NSFetchedResultsController的AZ索引,每个字母中都有单独的标头?

这是另一篇类似的文章,但是所有的Objective-c答案.

NSFetchedResultsController,其节由a的第一个字母创建字符串

更新::

找到了我认为与我的确切问题有关的另一篇文章(如何使用NSFetchedResultsController获得AZ索引)

解决方案

好吧,我想起来了,是个令人困惑的问题,需要进行大量研究.

好的,所以您要做的第一件事就是在数据模型上创建一个瞬态属性.就我而言,我称其为lettersection.要在实体中执行此操作,只需创建一个新属性并将其命名为lettersection,然后在图形模式下将其选中(双击),即可在检查器中看到瞬态"选项.这意味着它不会保存到数据库,而由于内部原因而被更多使用.

然后,您需要在模型定义的扩展区域中手动设置变量.这就是我的样子.

import Foundation
import CoreData

extension Inventory {

    @NSManaged var addCount: NSNumber?
    @NSManaged var barcode: String?
    @NSManaged var currentCount: NSNumber?
    @NSManaged var id: NSNumber?
    @NSManaged var imageLargePath: String?
    @NSManaged var imageSmallPath: String?
    @NSManaged var name: String?
    @NSManaged var negativeCount: NSNumber?
    @NSManaged var newCount: NSNumber?
    @NSManaged var store_id: NSNumber?
    @NSManaged var store: Store?

    var lettersection: String? {
        let characters = name!.characters.map { String($0) }
        return characters[0].uppercaseString
    }

}

执行完此操作后,您只需像这样用fetchedResultsController调用此新的"lettersection" ...

let frc = NSFetchedResultsController(
            fetchRequest: inventoryFetchRequest,
            managedObjectContext: self.moc,
            sectionNameKeyPath: "lettersection",
            cacheName: nil)

一切都会正常!它按我的库存物品的名称排序,但按首字母将它们分组,以获得一个不错的A,B,C类型列表!

Attached at very bottom of this question is my inventory controller file. My problem is I'm getting duplicate results in all the sections. I narrowed down the reason to

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

My code in that function does not account for how many rows there are in each section. As such I'm just printing out the same duplicate results every section.

The actual question is listed after the images below...

Refer to images below:

I also have the ability to change the index from my settings menu so it can index by numbers, like 0-9. Refer to image below:

That said, I currently load the data from Core Data. Attached is reference image of the entities I use and there relationships.

The Question:

My question is, how can I get the results from coreData to be sorted into the A,B,C type sections or 1,2,3 sections so that navigating the table will be simple.

My hunch is the line that says let inventoryRecords = try moc.executeFetchRequest(inventoryFetchRequest) as? [Inventory] needs a sort descriptor to sort based on how I like, but how I then take the data and put into the correct array structure to split into the sections I need...I have no idea.

globals.swift

import Foundation
import CoreData

//Array of Inventory & Store Core Data Managed Objects
var g_inventoryItems = [Inventory]()
var g_storeList = [Store]()
var g_appSettings = [AppSettings]()
var g_demoMode = false

InventoryController.swift

import UIKit
import CoreData

class InventoryController: UIViewController, UISearchBarDelegate, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet weak var searchBar: UISearchBar!
    @IBOutlet weak var inventoryTable: UITableView!

    var numberIndex = ["0","1","2","3","4","5","6","7","8","9"]
    var letterIndex = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]

    var moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext //convinience variable to access managed object context

    // Start DEMO Related Code
    func createInventoryDummyData(number: Int) -> Inventory{
        let tempInventory = NSEntityDescription.insertNewObjectForEntityForName("Inventory", inManagedObjectContext: moc) as! Inventory

        tempInventory.name = "Test Item # \(number)"
        tempInventory.barcode = "00000000\(number)"
        tempInventory.currentCount = 0
        tempInventory.id = number
        tempInventory.imageLargePath = "http://website.tech//uploads/inventory/7d3fe5bfad38a3545e80c73c1453e380.png"
        tempInventory.imageSmallPath = "http://website.tech//uploads/inventory/7d3fe5bfad38a3545e80c73c1453e380.png"
        tempInventory.addCount = 0
        tempInventory.negativeCount = 0
        tempInventory.newCount = 0
        tempInventory.store_id = 1 //belongs to same store for now

        //Select a random store to belong to 0 through 2 since array starts at 0
        let aRandomInt = Int.random(0...2)
        tempInventory.setValue(g_storeList[aRandomInt], forKey: "store") //assigns inventory to one of the stores we created.

        return tempInventory
    }

    func createStoreDummyData(number:Int) -> Store{
        let tempStore = NSEntityDescription.insertNewObjectForEntityForName("Store", inManagedObjectContext: moc) as! Store

        tempStore.address = "100\(number) lane, Miami, FL"
        tempStore.email = "store\(number)@centraltire.com"
        tempStore.id = number
        tempStore.lat = 1.00000007
        tempStore.lng = 1.00000008
        tempStore.name = "Store #\(number)"
        tempStore.phone = "123000000\(number)"

        return tempStore
    }

    // End DEMO Related Code

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.
        print("InventoryController -> ViewDidLoad -> ... starting inits")

        //First check to see if we have entities already.  There MUST be entities, even if its DEMO data.
        let inventoryFetchRequest = NSFetchRequest(entityName: "Inventory")
        let storeFetchRequest = NSFetchRequest(entityName: "Store")

        do {
            let storeRecords = try moc.executeFetchRequest(storeFetchRequest) as? [Store]
            if(storeRecords!.count<=0){
                g_demoMode = true
                print("No store entities found.  Demo mode = True.  Creating default store entities...")

                var store : Store //define variable as Store type

                for index in 1...3 {
                    store = createStoreDummyData(index)
                    g_storeList.append(store)
                }
            }

            let inventoryRecords = try moc.executeFetchRequest(inventoryFetchRequest) as? [Inventory]
            if(inventoryRecords!.count<=0){
                g_demoMode = true
                print("No entities found for inventory.  Demo mode = True.  Creating default entities...")

                var entity : Inventory //define variable as Inventory type

                for index in 1...20 {
                    entity = createInventoryDummyData(index)
                    g_inventoryItems.append(entity)
                }

                print("finished creating entities")
            }
        }catch{
            fatalError("bad things happened \(error)")
        }

        print("InventoryController -> viewDidload -> ... finished inits!")
    }

    override func viewWillAppear(animated: Bool) {
        print("view appearing")
        //When the view appears its important that the table is updated.

        //Look at the selected Store & Use the LIST of Inventory Under it.

        inventoryTable.reloadData()//this is important to update correctly for changes that might have been made
    }

    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        // Get the new view controller using segue.destinationViewController.
        // Pass the selected object to the new view controller.
        print("inventoryItemControllerPrepareForSegueCalled")

        if segue.identifier == "inventoryInfoSegue" {
            let vc = segue.destinationViewController as! InventoryItemController
            if let cell = sender as? InventoryTableViewCell{
                vc.inventoryItem = cell.inventoryItem! //sets the inventory item accordingly, passing its reference along.
            }else{
                print("sender was something else")
            }
        }

    }

    func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
        //This scrolls to correct section based on title of what was pressed.
        return letterIndex.indexOf(title)!
    }

    func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
        //Use correct index on the side based on settings desired.
        if(g_appSettings[0].indextype=="letter"){
            return letterIndex
        }else{
            return numberIndex
        }
    }

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

        //TODO: Need to figure out how many rows for ...column A,B,C or 1,2,3 based on indexType using~
        //To do this we need to organize the inventory results into a section'ed array.

        if(g_appSettings[0].selectedStore != nil){
            return (g_appSettings[0].selectedStore?.inventories!.count)! //number of rows is equal to the selected stores inventories count
        }else{
            return g_inventoryItems.count
        }
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("InventoryTableCell", forIndexPath: indexPath) as! InventoryTableViewCell

        if(g_appSettings[0].selectedStore != nil){
            //Get the current Inventory Item & Set to the cell for reference.
            cell.inventoryItem = g_appSettings[0].selectedStore?.inventories?.allObjects[indexPath.row] as! Inventory
        }else{
            //This only happens for DEMO mode or first time.
            cell.inventoryItem = g_inventoryItems[indexPath.row]//create reference to particular inventoryItem this represents.
        }

        cell.drawCell() //uses passed inventoryItem to draw it's self accordingly.

        return cell

    }

    func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        if(g_appSettings[0].indextype == "letter"){
            return letterIndex[section]
        }else{
            return numberIndex[section]
        }

    }

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        if(g_appSettings[0].selectedStore != nil){
            if(g_appSettings[0].indextype=="letter"){
                return letterIndex.count
            }else{
                return numberIndex.count
            }
        }else{
            return 1//only one section for DEMO mode.
        }
    }

    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        //dispatch_async(dispatch_get_main_queue()) {
            //[unowned self] in
            print("didSelectRowAtIndexPath")//does not recognize first time pressed item for some reason?
            let selectedCell = self.tableView(tableView, cellForRowAtIndexPath: indexPath) as? InventoryTableViewCell
            self.performSegueWithIdentifier("inventoryInfoSegue", sender: selectedCell)
        //}

    }


    @IBAction func BarcodeScanBarItemAction(sender: UIBarButtonItem) {
        print("test of baritem")
    }
    @IBAction func SetStoreBarItemAction(sender: UIBarButtonItem) {
        print("change store interface")
    }

    func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
        print("text is changing")
    }

    func searchBarCancelButtonClicked(searchBar: UISearchBar) {
        print("ended by cancel")
        searchBar.text = ""
        searchBar.resignFirstResponder()
    }

    func searchBarSearchButtonClicked(searchBar: UISearchBar) {
        print("ended by search")
        searchBar.resignFirstResponder()
    }

    func searchBarTextDidEndEditing(searchBar: UISearchBar) {
        print("ended by end editing")
        searchBar.resignFirstResponder()
    }

    @IBAction func unwindBackToInventory(segue: UIStoryboardSegue) {
        print("unwind attempt")

        let barcode = (segue.sourceViewController as? ScannerViewController)?.barcode
        searchBar.text = barcode!

        print("barcode="+barcode!)

        inventoryTable.reloadData()//reload the data to be safe.

    }

}

//Extention to INT to create random number in range.
extension Int
{
    static func random(range: Range<Int> ) -> Int
    {
        var offset = 0

        if range.startIndex < 0   // allow negative ranges
        {
            offset = abs(range.startIndex)
        }

        let mini = UInt32(range.startIndex + offset)
        let maxi = UInt32(range.endIndex   + offset)

        return Int(mini + arc4random_uniform(maxi - mini)) - offset
    }
}

Update:: **

So I was looking around and found this article (I implemented it).

https://www.andrewcbancroft.com/2015/03/05/displaying-data-with-nsfetchedresultscontroller-and-swift/

I'm really close now to figuring it out. Only problem is I can get it to auto create the sections, but only on another field, like for example store.name, I can't get it to section it into A,B,C sections or 1,2,3.

This is my code for the fetchedResultsController using the methods described in that article.

//Create fetchedResultsController to handle Inventory Core Data Operations
    lazy var fetchedResultsController: NSFetchedResultsController = {
        let inventoryFetchRequest = NSFetchRequest(entityName: "Inventory")
        let primarySortDescriptor = NSSortDescriptor(key: "name", ascending: true)
        let secondarySortDescriptor = NSSortDescriptor(key: "barcode", ascending: true)
        inventoryFetchRequest.sortDescriptors = [primarySortDescriptor, secondarySortDescriptor]

        let frc = NSFetchedResultsController(
            fetchRequest: inventoryFetchRequest,
            managedObjectContext: self.moc,
            sectionNameKeyPath: "store.name",
            cacheName: nil)

        frc.delegate = self

        return frc
    }()

Question is what to put for sectionNameKeyPath: now that will make it section it on A B C and I got this !

Found a stackoverflow post very similar to my issue, but need swift answer.

A-Z Index from NSFetchedResultsController with individual section headers within each letter?

Here is another similar article but all objective-c answers.

NSFetchedResultsController with sections created by first letter of a string

Update::

Found another article I think with my exact issue (How to have a A-Z index with a NSFetchedResultsController)

解决方案

Ok I figured it out, phew was this confusing and took a lot of research.

Okay, so first thing you have to do is create a transient property on the data model. In my case I called it lettersection. To do this in the entity just create a new attribute and call it lettersection and in graph mode if you select it (double click it), you will see option in inspector for 'transient'. This means it won't be saved to the database and is used more for internal reasons.

You then need to manually set up the variable in the extension area of the model definition. Here is how it looks for me.

import Foundation
import CoreData

extension Inventory {

    @NSManaged var addCount: NSNumber?
    @NSManaged var barcode: String?
    @NSManaged var currentCount: NSNumber?
    @NSManaged var id: NSNumber?
    @NSManaged var imageLargePath: String?
    @NSManaged var imageSmallPath: String?
    @NSManaged var name: String?
    @NSManaged var negativeCount: NSNumber?
    @NSManaged var newCount: NSNumber?
    @NSManaged var store_id: NSNumber?
    @NSManaged var store: Store?

    var lettersection: String? {
        let characters = name!.characters.map { String($0) }
        return characters[0].uppercaseString
    }

}

Once you do this, you simply call this new 'lettersection' with the fetchedResultsController like so...

let frc = NSFetchedResultsController(
            fetchRequest: inventoryFetchRequest,
            managedObjectContext: self.moc,
            sectionNameKeyPath: "lettersection",
            cacheName: nil)

and everything will work! It sorts by the name of my inventory items, but groups them by the first letters, for a nice A,B,C type list!

这篇关于如何使用CoreData结果快速在UITableview的正确部分中排序和显示的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆