macOS开发:Storyboard创建的副窗口TableView数据更新不生效如何解决
问题根本原因
NSTableView 不会自动监听数据源的变化,仅会在初始化加载、手动调用reloadData()相关方法时才会重新读取数据源内容渲染界面。如果你在视图控制器的 view 加载完成之后修改info属性,tableView 没有收到刷新通知,自然不会更新展示内容。
现有方案的合理性
你当前自定义updateInfoTableView方法的实现完全符合行业规范,封装性很好,外部调用方不需要知道视图控制器内部存在 tableView,只需要调用暴露的更新方法即可,符合面向对象的封装原则。
更简洁的可选优化方案
如果你的info属性会在多个场景下被修改,可以给属性添加didSet观察者,自动触发刷新,不需要额外定义更新方法:
public var info : [String] = ["Default", "Test", "Data"] { didSet { // 仅在视图已加载的情况下调用刷新,避免不必要的性能消耗和潜在崩溃 guard isViewLoaded else { return } infoTableView.reloadData() } }
添加后你可以直接在外部赋值vc!.info = types,不管赋值发生在视图加载前还是加载后都能正常生效:
- 若赋值时视图还未加载,视图加载时 tableView 会直接读取最新的
info值渲染 - 若赋值时视图已加载,
didSet会自动触发 tableView 刷新
现有代码的隐藏问题修复
你当前将batchesWindowControllers声明在 for 循环内部,属于局部变量,循环结束后该变量会被回收,你存在数组中的NSWindowController没有强引用持有,会被 ARC 自动释放,可能导致刚打开的窗口意外关闭。你需要将batchesWindowControllers声明为当前调用类的成员属性,持有所有创建的窗口控制器:
// 在你的主视图控制器类中声明为成员属性 var batchesWindowControllers: [NSWindowController] = []
删除 for 循环内的var batchesWindowControllers : [NSWindowController] = []这行代码即可。
关于Segue的疑问说明
prepare(for:sender:)仅适用于通过 Storyboard Segue 触发的控制器跳转,本质也是在目标控制器视图加载前给属性赋值,和你现在手动实例化控制器后赋值属性的逻辑没有区别。如果用 Segue 创建窗口后还需要动态修改info的值,同样需要触发 tableView 刷新。
最终结论
两种实现方案都是行业常用的规范实现,如果你仅需要在创建窗口时赋值一次数据,两种方案都可以;如果你需要在窗口打开后多次更新数据,带didSet的属性写法更简洁,自定义更新方法的封装性更强,按需选择即可。
内容的提问来源于stack exchange,提问作者psb

