如何使用CoreData结果在swift中对UITableview的正确部分进行排序和显示

时间:2016-04-10 00:02:41

标签: ios swift uitableview sorting core-data

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

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

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

实际问题列在下面的图片 ...

之后

请参阅以下图片:

enter image description here enter image description here

我还能够从设置菜单更改索引,以便按数字编制索引,如0-9。请参考下图:

enter image description here

那就是说,我目前从Core Data加载数据。附件是我使用的实体的参考图像和那里的关系。

enter image description here

问题:

我的问题是,如何将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帖子,但需要快速回答。

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

这是另一篇类似的文章,但所有客观的答案都是。

NSFetchedResultsController with sections created by first letter of a string

更新::

在我的确切问题(How to have a A-Z index with a NSFetchedResultsController

中找到另一篇文章

2 个答案:

答案 0 :(得分:1)

&#34;我的问题是,如何将coreData的结果分类到A,B,C类型的部分或1,2,3部分,以便导航表格很简单。&#34 ;

使用&#34;存储&#34;作为您的实体和财产&#34; name&#34;是你想要按记录排序的。

    override func viewDidLoad() { super.viewDidLoad()

        let fetchRequest = NSFetchRequest() 
        let entity = NSEntityDescription.entityForName("Store", inManagedObjectContext: managedObjectContext)
        fetchRequest.entity = entity

        let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
        fetchRequest.sortDescriptors = [sortDescriptor]
        do { 
              let foundObjects = try managedObjectContext.executeFetchRequest(fetchRequest)

              locations = foundObjects as! [Location]
        } catch {
              fatalCoreDataError(error) }
  }

您将使用此功能设置部分数量:

func numberOfSectionsInTableView(tableView: UITableView) -> Int {

return letterindex.count // if that is how you want to construct sections
}

我从Ray Wenderlich电子书&#34; iOS Apprentice&#34;中学到了这一点。从第3课 - MyLocations。强烈推荐这本以及他们关于CoreData的电子书。

答案 1 :(得分:1)

好的,我想通了,phew让人感到困惑,并进行了大量的研究。

好的,首先要做的是在数据模型上创建一个瞬态属性。在我的情况下,我称之为letterection。要在实体中执行此操作,只需创建一个新属性并将其命名为lettersection,如果选择它,则以图形模式(双击它),您将在检查器中看到&amp;瞬态&#39;中的选项。这意味着它不会被保存到数据库中,并且由于内部原因而被更多地使用。

然后,您需要在模型定义的扩展区域中手动设置变量。以下是它的寻找方式。

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
    }

}

一旦你这样做,你只需要打电话给这个新的&#39;字母&#39;与fetchedResultsController一样......

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

一切都会奏效!它按我的库存物品的名称排序,但是按照第一个字母对它们进行分组,以获得漂亮的A,B,C类型列表!