初始化并填充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
记录的集合将分别键入teeColour
和Course
,Scorecard
是
为(由appearsOn
标识)。
在运行时,“分数编辑器”屏幕将显示发球区域颜色和关联的Hole
记录表。
提供的发球区颜色未更改,功能按预期工作。
但是,如果更改了发球区域的颜色,则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
的行为,因为两者之间似乎没有任何联系。
这是怎么回事?