0

我有一个 macOS 应用程序——不是基于文档的——它使用 Cocoa 绑定、核心数据和故事板。数据模型很简单...

List
  Name
  Players; to many relationship to Player

Player
  Name
  Lists; to-many relationship to List

故事板具有以下布局...

Window Controller
  Split View Controller
    View Controller
      Table View
    View Controller
      Table View
    View Controller
      Label

我想弄清楚的是如何在三个视图控制器之间正确共享托管对象上下文,并使两个表视图和标签保持同步。使用这个问题的答案,我有一些几乎可以使用的东西,尽管略有不同,因为我有一个多对多的关系,所以需要一个额外的表格视图。

我已经设置了一个中间控制器对象,它具有对托管对象上下文的引用、两个用于使各种数组控制器保持同步的索引数组以及初始排序描述符。

class Controller: NSObject {
  @objc var moc = ...
  @objc var listSelectionIndexes = IndexSet()
  @objc var playerSelectionIndexes = IndexSet()
  @objc var sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
} 

然后我有一个子类NSSplitViewController,我用来将这个控制器注入三个视图控制器......

class SplitViewController: NSSplitViewController {
    private let controller = Controller()

    override func awakeFromNib() {
        super.awakeFromNib()
        children.forEach {
            $0.representedObject = controller
        }
    }
}

在最左边的控制器中,我有一个数组控制器,它设置为保留其选择并准备其内容,并具有以下绑定...

[Entity: List]
Selection Indexes ~> View Controller.self.representedObject.listSelectionIndexes
Managed Object Context ~> View Controller.self.representedObject.moc

和相应的表格视图...

Content ~> Array Controller.arrangedObjects
Selection Indexes ~> Array Controller.selectionIndexes
Sort Descriptors ~> Array Controller.sortDescriptors

在中间控制器中,我有两个数组控制器,它们都设置为保留它们的选择并准备它们的内容,并具有以下绑定......

[Array Controller 1; Entity: List]
Selection Indexes ~> View Controller.self.representedObject.listSelectionIndexes
Managed Object Context ~> View Controller.self.representedObject.moc

[Array Controller 2; Entity: Player]
Content Set ~> Array Controller 1.selection.players
Selection Indexes ~> View Controller.self.representedObject.playerSelectionIndexes
Sort Descriptors ~> View Controller.self.representedObject.sortDescriptors
Managed Object Context ~> View Controller.self.representedObject.moc

以及绑定到数组控制器2的相应表视图...

Content ~> Array Controller 2.arrangedObjects
Selection Indexes ~> Array Controller 2.selectionIndexes
Sort Descriptors ~> Array Controller 2.sortDescriptors

最后,最右边的视图控制器,我有一个数组控制器,它设置为准备其内容但保留选择,并具有以下绑定...

[Entity: Player]
Selection Indexes ~> View Controller.self.representedObject.playerSelectionIndexes
Sort Descriptors ~> View Controller.self.representedObject.sortDescriptors
Managed Object Context ~> View Controller.self.representedObject.moc

标签有一个单一的绑定......

Value ~> Array Controller.selection.name

您可以在此屏幕截图中看到事情几乎可以正常工作... 在此处输入图像描述 我说几乎是因为如果我使用其标题对中间表进行排序,即使保留了中间选择,我也会丢失最右边的选择... 在此处输入图像描述 另外,如果我单击已选择的行,它不会更新第三个窗格 - 我必须单击关闭然后再次单击以使其更新。选择任何其他行按预期工作。

如果然后我在最右边的视图控制器中的数组控制器上启用保留选择,则会保留选择,但这是错误的,因为看起来排序顺序没有同步...... 在此处输入图像描述

如何解决此问题:最右侧视图控制器中的排序顺序和选择?

还有一个更普遍的问题,这真的是最好的方法吗?这似乎是一个可怕的管道——几乎感觉就像一个黑客——只是为了能够使用绑定在场景之间保持数据同步,而且我还担心使用多个数组控制器的成本都持有相同的信息,尤其是如果有数千条记录。

4

1 回答 1

0

与其添加数组控制器来重建选定的列表或播放器,不如将选定的列表和播放器转移到另一个视图控制器更直接。当表格视图中的选择发生变化时,它需要一些代码来更改所选列表和播放器。

class Controller: NSObject {
    @objc dynamic var moc = …
    @objc dynamic weak var selectedList : List?
    @objc dynamic weak var selectedPlayer : Player?
}

最左边的列表视图控制器:

视图控制器是表视图的委托。

class ListViewController: NSViewController, NSTableViewDelegate {

    @IBOutlet var arrayController: NSArrayController!

    func tableViewSelectionDidChange(_ notification: Notification) {
        let controller = representedObject as! Controller
        if arrayController.selectedObjects.count == 1,
            let list = arrayController.selectedObjects[0] as? List {
            controller.selectedList = list
        }
        else {
            controller.selectedList = nil
            controller.selectedPlayer = nil // tableViewSelectionDidChange isn't called on the Player table view
        }
    }

}

中间播放器视图控制器:

视图控制器是表视图的委托。

class PlayerViewController: NSViewController, NSTableViewDelegate {
    
    @IBOutlet var arrayController: NSArrayController!

    func tableViewSelectionDidChange(_ notification: Notification) {
        let controller = representedObject as! Controller
        if arrayController.selectedObjects.count == 1,
            let player = arrayController.selectedObjects[0] as? Player {
            controller.selectedPlayer = player
        }
        else {
            controller.selectedPlayer = nil
        }
    }
    
}

将 Player 数组控制器的 Content Set 绑定到View Controller.representedObject.selectedList.players

最右边的细节视图控制器:

将文本字段的值绑定到View Controller.representedObject.selectedPlayer.name

于 2020-06-25T07:19:29.153 回答