NSFetchedResultsController在应为“更新”时识别出“删除”

时间:2018-10-12 22:10:27

标签: swift nsfetchedresultscontroller

初始化并填充FRC之后,对(显然)自变量的更新似乎会改变FRC的预期行为。

我正在构建与高尔夫相关的应用,并且在将Scorecard对象管理为以下视图的视图中定义了FRC:

fileprivate lazy var fetchedResultsController: NSFetchedResultsController<Hole> = {

    let fetchRequest: NSFetchRequest<Hole> = Hole.fetchRequest()

    self.teeColourString = self.scorecard?.value(forKey: "teeColour") as! String?

    fetchRequest.predicate = NSPredicate(format: "%K == %@ AND %K == %@", "appearsOn.offeredAt", (self.course)!, "appearsOn.teeColour", self.teeColourString!)

    fetchRequest.sortDescriptors = [NSSortDescriptor(key: "holeNumber", ascending: true)]

    let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: coreDataManager.mainManagedObjectContext, sectionNameKeyPath: nil, cacheName: nil)

    // Configure Fetched Results Controller
    fetchedResultsController.delegate = self

    return fetchedResultsController
}()

基础数据模型要求记分卡(Hole对象)必须有18个孔(Scorecard对象),并且没有它们就无法创建记分卡。每个高尔夫球场可以根据所考虑的发球区颜色使用多个记分卡。对于每个Scorecard,其18条Hole记录的集合将分别键入teeColourCourseScorecard是 为(由appearsOn标识)。

在运行时,“分数编辑器”屏幕将显示发球区域颜色和关联的Hole记录表。

Scorecard Editor

提供的发球区颜色未更改,功能按预期工作。

但是,如果更改了发球区域的颜色,则FRC会将对任何Hole记录的任何后续编辑都视为update而不是delete

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {

    switch type {
    case .update:
        holesTable.reloadRows(at: [indexPath!], with: .automatic)
        lookForSIProblems(holes: fetchedResultsController.fetchedObjects!, saveMode: false)
        saveButton.isEnabled = true
    case .insert:
        holesTable.insertRows(at: [newIndexPath!], with: .automatic)
    case .delete:
        break
    case .move:
        holesTable.moveRow(at: indexPath! as IndexPath, to: newIndexPath! as IndexPath)
    }
}

XCode终端窗口将报告:

[error] fault: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (17) must be equal to the number of rows contained in that section before the update (18), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)

通过按屏幕上标记为“三通颜色”的UIButton来实现三通颜色的更新。这将启动一个弹出式滚动视图,用户可以从中选择新的T恤颜色。然后,结果选择将更新按钮旁边的UILabel的背景颜色,以显示当前的三通色。

用于选择发球区域颜色的UIButton具有以下IBAction代码:

@IBAction func showPopUpColourPicker(_ sender: UIButton) {
    // User is choosing the tee Colour for the scorecard
    let popOverVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "sbPopUpID") as! PopUpViewController

    popOverVC.modalPresentationStyle = .popover

    popOverVC.popoverPresentationController?.sourceView = sender
    popOverVC.popoverPresentationController?.sourceRect = sender.bounds

    popOverVC.popoverPresentationController!.delegate = self

    popOverVC.completionHandler = {(chosenTeeColour : String?) in
        if let valueSelected = chosenTeeColour
        {
            self.saveButton.isEnabled = true
            self.teeColourString = valueSelected
            self.scorecard?.setValue(valueSelected, forKey: "teeColour") // Make this the default for all new scorecards
            self.teeColourString = (self.scorecard?.value(forKey: "teeColour") as! String)

            self.teeColour.setup(teeColour: self.teeColourString!)
        }
    }

    popOverVC.preferredContentSize = CGSize(width: 320, height: 100)

    present(popOverVC, animated: true, completion: nil)

}

依次将类PopUpViewController定义为:

class PopUpViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

@IBOutlet weak var teeBoxPicker: UIPickerView!

var completionHandler : ((String?)->(Void))?
var chosenTeeColour: String?
var pickerData: [String] = [String]()


override func viewDidLoad() {
    super.viewDidLoad()

    self.view.backgroundColor = UIColor.black.withAlphaComponent(0.8)

    // Connect data:
    self.teeBoxPicker.delegate = self
    self.teeBoxPicker.dataSource = self

    pickerData = ["Red", "Blue", "Yellow", "White", "Green", "Black"]

}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

// The number of columns of data
func numberOfComponents(in pickerView: UIPickerView) -> Int {
    return 1
}

// The number of rows of data
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    return pickerData.count
}

// The data to return for the row and component (column) that's being passed in
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
    return pickerData[row]
}

// Retrieve the selected value and force lowercase for insertion into database
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {

    let valueSelected = pickerData[row] as String

    chosenTeeColour = valueSelected.lowercased()

    self.dismiss(animated: true, completion: {[weak self] in
        if let handler = self?.completionHandler
        {
            handler(self?.chosenTeeColour)
        }
    })


    self.view.removeFromSuperview()
}

func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {

    var labelBackgroundColour: UIColor
    var labelTextColor: UIColor

    let teeColour = pickerData[row]
    let pickerLabel = UILabel()

    labelBackgroundColour = getTeeColour(teeDescriptor: teeColour.lowercased())

    if labelBackgroundColour == .white || labelBackgroundColour == .yellow {
        labelTextColor = UIColor.black
    } else {
        labelTextColor = UIColor.white
    }

    pickerLabel.backgroundColor = labelBackgroundColour
    pickerLabel.textColor = labelTextColor

    let myTitle = NSAttributedString(string: teeColour, attributes: [NSAttributedStringKey.foregroundColor:labelTextColor])
    pickerLabel.attributedText = myTitle
    pickerLabel.textAlignment = .center

    return pickerLabel
  }
}

我无法确定更新Tee Color应该如何改变FRC或其UITableView的行为,因为两者之间似乎没有任何联系。

这是怎么回事?

0 个答案:

没有答案
相关问题