在 macOS Cocoa 应用程序中有两个相同的 NSOutlineViews 并排,有没有办法在两者之间同步展开/折叠项目?
In a macOS Cocoa Application that has two identical NSOutlineViews side by side, is there a way to expand / collapse items in sync between the two?
我正在 Swift 5.1 中开发 macOS Cocoa 应用程序。在主要 window 中,我有两个相同的 NSOutlineView,它们的内容完全相同。我想启用同步模式,如果两个 NSOutlineViews 之一中的项目为 expanded/collapsed,则相应的项目同时在另一个中为 expanded/collapsed。我试图通过在委托中实现 shouldExpandItem 和 shouldCollapseItem 来做到这一点。委托是两个 NSOutlineViews 的同一个对象,我有引用两个 NSOutlineViews 的出口来区分两者。问题是,如果我在 shouldExpandItem 中以编程方式调用 expandItem,则会再次为另一个 NSOutlineView 调用该方法,从而导致无限循环和堆栈溢出。我找到了一个可行的肮脏解决方案,方法是暂时将相关 NSOutlineView 的委托设置为 nil,expand/collapse 该项目,然后将委托设置回去。代码如下:
func outlineView(_ outlineView: NSOutlineView, shouldExpandItem item: Any) -> Bool {
let filePath = (item as! FileSystemObject).fullPath
let trueItem = item as! FileSystemObject
trueItem.children = Array()
do {
let contents = try FileManager.default.contentsOfDirectory(atPath: filePath!)
for (_, item) in contents.enumerated() {
let entry = FileSystemObject.init()
entry.fullPath = URL.init(fileURLWithPath: filePath!).appendingPathComponent(item).path
if entry.exists {
trueItem.children.append(entry)
}
}
} catch {
}
if outlineView == self.leftOutlineView {
self.rightOutlineView.delegate = nil;
self.rightOutlineView.expandItem(item)
self.rightOutlineView.delegate = self;
} else {
self.leftOutlineView.delegate = nil;
self.leftOutlineView.expandItem(item)
self.leftOutlineView.delegate = self;
}
return true
}
func outlineView(_ outlineView: NSOutlineView, shouldCollapseItem item: Any) -> Bool {
if outlineView == self.leftOutlineView {
self.rightOutlineView.delegate = nil;
self.rightOutlineView.collapseItem(item)
self.rightOutlineView.delegate = self;
} else {
self.leftOutlineView.delegate = nil;
self.leftOutlineView.collapseItem(item)
self.leftOutlineView.delegate = self;
}
return true
}
这似乎可行,但我担心这种方法可能会出错。暂时将代表设置为 nil 是一种可能的解决方案,还是我应该注意任何注意事项?您是否可以建议另一种模式来实现这一目标?谢谢
编辑:根据以下评论和答案
感谢我收到的答案/评论,我找到了最简单的解决方案。
可以实现 outlineViewDidExpand 和 outlineViewDidCollapse 并将同步逻辑放在那里,而不是在 outlineViewShouldExpand/Collapse 方法中实现同步逻辑。当以编程方式 expanding/collapsing 项目时,不会调用后一种方法,因此不存在无限循环或堆栈溢出的风险。
代码如下:
func outlineViewItemDidExpand(_ notification: Notification) {
let outlineView = notification.object as! NSOutlineView
let userInfo = notification.userInfo as! Dictionary<String, Any>
let item = userInfo["NSObject"]
if outlineView == self.leftOutlineView {
self.rightOutlineView.animator().expandItem(item)
} else {
self.leftOutlineView.animator().expandItem(item)
}
}
func outlineViewItemDidCollapse(_ notification: Notification) {
let outlineView = notification.object as! NSOutlineView
let userInfo = notification.userInfo as! Dictionary<String, Any>
let item = userInfo["NSObject"]
if outlineView == self.leftOutlineView {
self.rightOutlineView.animator().collapseItem(item)
} else {
self.leftOutlineView.animator().collapseItem(item)
}
}
此外,现在,我不明白为什么项目的展开/折叠是动画的,这与我原来的方法不起作用。
我希望这对某人有帮助,这对我很有帮助。非常感谢。
我的应用程序执行类似的操作。我需要在 windows 中保持许多视图同步。其中一个视图是 NSOutlineView
。 运行 我对 NSOutlineView
有一些怪癖,但我认为它们与同步无关。
不过,我确实采取了不同的方法。我没有操纵委托,而是有一个抑制某些动作的标志。在我的例子中,一个标志更有意义,因为许多其他视图都受到影响。但是,效果和你做的很相似。
我认为唯一的风险是在同步期间错过委托调用。假设你的逻辑不依赖于此,我相信你的方法会很好。
outlineView(_:shouldExpandItem:)
在项目展开之前调用,来回调用 expandItem()
会导致无限循环。 outlineViewItemDidExpand(_:)
在项目展开后调用,而 NSOutlineView.expandItem(_:)
在项目已经展开时不执行任何操作(已记录的行为)。展开左大纲视图时,右大纲视图的expandItem()
确实调用了outlineViewItemDidExpand(_:)
,但左大纲视图的expandItem()
不会再次调用outlineViewItemDidExpand(_:)
。
func outlineViewItemDidExpand(_ notification: Notification) {
let outlineView = notification.object as! NSOutlineView
let item = notification.userInfo!["NSObject"]
if outlineView == self.leftOutlineView {
self.rightOutlineView.animator().expandItem(item)
} else {
self.leftOutlineView.animator().expandItem(item)
}
}
我正在 Swift 5.1 中开发 macOS Cocoa 应用程序。在主要 window 中,我有两个相同的 NSOutlineView,它们的内容完全相同。我想启用同步模式,如果两个 NSOutlineViews 之一中的项目为 expanded/collapsed,则相应的项目同时在另一个中为 expanded/collapsed。我试图通过在委托中实现 shouldExpandItem 和 shouldCollapseItem 来做到这一点。委托是两个 NSOutlineViews 的同一个对象,我有引用两个 NSOutlineViews 的出口来区分两者。问题是,如果我在 shouldExpandItem 中以编程方式调用 expandItem,则会再次为另一个 NSOutlineView 调用该方法,从而导致无限循环和堆栈溢出。我找到了一个可行的肮脏解决方案,方法是暂时将相关 NSOutlineView 的委托设置为 nil,expand/collapse 该项目,然后将委托设置回去。代码如下:
func outlineView(_ outlineView: NSOutlineView, shouldExpandItem item: Any) -> Bool {
let filePath = (item as! FileSystemObject).fullPath
let trueItem = item as! FileSystemObject
trueItem.children = Array()
do {
let contents = try FileManager.default.contentsOfDirectory(atPath: filePath!)
for (_, item) in contents.enumerated() {
let entry = FileSystemObject.init()
entry.fullPath = URL.init(fileURLWithPath: filePath!).appendingPathComponent(item).path
if entry.exists {
trueItem.children.append(entry)
}
}
} catch {
}
if outlineView == self.leftOutlineView {
self.rightOutlineView.delegate = nil;
self.rightOutlineView.expandItem(item)
self.rightOutlineView.delegate = self;
} else {
self.leftOutlineView.delegate = nil;
self.leftOutlineView.expandItem(item)
self.leftOutlineView.delegate = self;
}
return true
}
func outlineView(_ outlineView: NSOutlineView, shouldCollapseItem item: Any) -> Bool {
if outlineView == self.leftOutlineView {
self.rightOutlineView.delegate = nil;
self.rightOutlineView.collapseItem(item)
self.rightOutlineView.delegate = self;
} else {
self.leftOutlineView.delegate = nil;
self.leftOutlineView.collapseItem(item)
self.leftOutlineView.delegate = self;
}
return true
}
这似乎可行,但我担心这种方法可能会出错。暂时将代表设置为 nil 是一种可能的解决方案,还是我应该注意任何注意事项?您是否可以建议另一种模式来实现这一目标?谢谢
编辑:根据以下评论和答案
感谢我收到的答案/评论,我找到了最简单的解决方案。 可以实现 outlineViewDidExpand 和 outlineViewDidCollapse 并将同步逻辑放在那里,而不是在 outlineViewShouldExpand/Collapse 方法中实现同步逻辑。当以编程方式 expanding/collapsing 项目时,不会调用后一种方法,因此不存在无限循环或堆栈溢出的风险。
代码如下:
func outlineViewItemDidExpand(_ notification: Notification) {
let outlineView = notification.object as! NSOutlineView
let userInfo = notification.userInfo as! Dictionary<String, Any>
let item = userInfo["NSObject"]
if outlineView == self.leftOutlineView {
self.rightOutlineView.animator().expandItem(item)
} else {
self.leftOutlineView.animator().expandItem(item)
}
}
func outlineViewItemDidCollapse(_ notification: Notification) {
let outlineView = notification.object as! NSOutlineView
let userInfo = notification.userInfo as! Dictionary<String, Any>
let item = userInfo["NSObject"]
if outlineView == self.leftOutlineView {
self.rightOutlineView.animator().collapseItem(item)
} else {
self.leftOutlineView.animator().collapseItem(item)
}
}
此外,现在,我不明白为什么项目的展开/折叠是动画的,这与我原来的方法不起作用。 我希望这对某人有帮助,这对我很有帮助。非常感谢。
我的应用程序执行类似的操作。我需要在 windows 中保持许多视图同步。其中一个视图是 NSOutlineView
。 运行 我对 NSOutlineView
有一些怪癖,但我认为它们与同步无关。
不过,我确实采取了不同的方法。我没有操纵委托,而是有一个抑制某些动作的标志。在我的例子中,一个标志更有意义,因为许多其他视图都受到影响。但是,效果和你做的很相似。
我认为唯一的风险是在同步期间错过委托调用。假设你的逻辑不依赖于此,我相信你的方法会很好。
outlineView(_:shouldExpandItem:)
在项目展开之前调用,来回调用 expandItem()
会导致无限循环。 outlineViewItemDidExpand(_:)
在项目展开后调用,而 NSOutlineView.expandItem(_:)
在项目已经展开时不执行任何操作(已记录的行为)。展开左大纲视图时,右大纲视图的expandItem()
确实调用了outlineViewItemDidExpand(_:)
,但左大纲视图的expandItem()
不会再次调用outlineViewItemDidExpand(_:)
。
func outlineViewItemDidExpand(_ notification: Notification) {
let outlineView = notification.object as! NSOutlineView
let item = notification.userInfo!["NSObject"]
if outlineView == self.leftOutlineView {
self.rightOutlineView.animator().expandItem(item)
} else {
self.leftOutlineView.animator().expandItem(item)
}
}