Big Sur 大纲视图可扩展项目损坏
Posted
技术标签:
【中文标题】Big Sur 大纲视图可扩展项目损坏【英文标题】:Big Sur outline view expandable items broken 【发布时间】:2020-11-15 12:38:09 【问题描述】:我已经启动了一个新的 macOS 项目(目前在 Big Sur beta 3 上),NSOutlineView
节点似乎已损坏。不知道这是我还是操作系统。
Here's a sample project 证明了这个问题。还有一张图……
如您所见,单元格与扩展人字形重叠。单击任一 V 形将第一行恢复为正确的布局,但不是第二行。此外,永远不会调用自动保存方法 persistentObjectForItem
和 itemForPersistentObject
。
测试项目非常简单——我所做的只是将视图库中的SourceView
组件添加到默认应用项目中,并将委托/数据源连接到视图控制器。还检查了 IB 中的 Autosave Expanded Items
并在 Autosave
字段中输入名称。这是整个控制器代码:
class ViewController: NSViewController
@IBOutlet var outlineView: NSOutlineView?
let data = [Node("First item", 1), Node("Second item", 2)]
extension ViewController: NSOutlineViewDataSource
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any
data[index]
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool
true
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int
item == nil ? data.count : 0
func outlineView(_ outlineView: NSOutlineView, objectValueFor tableColumn: NSTableColumn?, byItem item: Any?) -> Any?
item
func outlineView(_ outlineView: NSOutlineView, persistentObjectForItem item: Any?) -> Any?
(item as? Node)?.id
func outlineView(_ outlineView: NSOutlineView, itemForPersistentObject object: Any) -> Any?
guard let id = object as? Int else return nil
return data.first $0.id == id
extension ViewController: NSOutlineViewDelegate
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView?
guard let node = item as? Node else
preconditionFailure("Invalid data item \(item)")
let view = outlineView.makeView(withIdentifier: nodeCellIdentifier, owner: self) as? NSTableCellView
view?.textField?.stringValue = node.name
view?.imageView?.image = NSImage(systemSymbolName: node.icon, accessibilityDescription: nil)
return view
final class Node
let id: Int
let name: String
let icon: String
init(_ name: String, _ id: Int, _ icon: String = "folder")
self.id = id
self.name = name
self.icon = icon
private let nodeCellIdentifier = NSUserInterfaceItemIdentifier("DataCell")
有没有 Mac 开发人员可以提供帮助?
【问题讨论】:
打开你的项目,选择大纲视图并将highlight属性值设置为Regular(这里有Source List)。 感谢@zrzka,但这是为了在侧边栏中,所以SourceList
是我正在寻找的风格。你是对的,改变风格可以解决问题,所以这可能证实它是一个错误?此外,仍然不会调用扩展状态的自动保存
你是否有一些你想要实现的截图?最终目标是什么?因为我没有在您的代码中看到 outlineView(_ outlineView: NSOutlineView, isGroupItem item: Any)
实现等。源列表的行为有点不同 - 例如 - 项目从第 3 级缩进,......通常的结构类似于 - *** = 组,第 2 级= 没有缩进的节点,可能有子节点,第 3 级 = 缩进的节点,...
两个级别的 Finder,例如 - gist 和 screenshot。三个级别 - 相同的要点和screenshot。附言不要强行展开 (!
),我用它来快速说明我的意思。
还跳转到NSOutlineView.SelectionHighlightStyle.sourceList
的定义 并阅读那里的 cmets(它们不包含在文档中)。
【参考方案1】:
来源列表
什么是源列表?它是NSOutlineView
(它是
NSTableView
) 有一个特殊的
治疗。 Finder 截图:
要创建源列表,您所要做的就是设置
selectionHighlightStyle
属性
.sourceList
。文档说:
NSTableView 的源列表样式。在 10.5 上,浅蓝色渐变用于突出显示选定的行。
它到底是做什么的? 在 Xcode 中跳转到定义并读取 cmets(未包含在文档中):
NSTableView 的源列表样式。在 10.10 及更高版本中,模糊选择用于突出显示行。在此之前,使用了浅蓝色渐变。注意:具有 drawsBackground 属性的单元格应将其设置为 NO。否则,它们将覆盖 NSTableView 所做的突出显示。设置此样式会产生将背景颜色设置为“源列表”背景颜色的副作用。此外,在 NSOutlineView 中,更改了以下属性以获得标准的“源列表”外观:indentationPerLevel、rowHeight 和 intercellSpacing。调用 setSelectionHighlightStyle: 后,可以根据需要更改任何其他属性。在 10.11 中,如果背景颜色已从“源列表”背景颜色更改为其他颜色,则表格将不再将所选内容绘制为源列表模糊样式,而是进行正常的蓝色突出显示。
由于您使用的是 Big Sur,请注意 SelectionHighlightStyle.sourceList
已弃用。
应该使用style
&effectiveStyle
.
示例项目
Xcode:
新项目 macOS 和应用程序(Storyboard 和 AppKit 应用程序委托和 Swift) Main.storyboard 添加源列表控件 定位和修复约束 将委托和数据源设置为 ViewController 启用自动保存展开的项目 将自动保存设置为您想要的任何内容(我有FinderLikeSidebar
那里)
明智选择,因为展开状态保存在用户默认值中
在NSOutlineView Items FinderLikeSidebar
键下
创建@IBOutlet var outlineView: NSOutlineView!
添加另一个文本表格单元格视图(无图像)
将标识符设置为GroupCell
ViewController.swift
下面的注释代码
截图
如您所见,它几乎就像 Finder 一样 - 第二级仍然是缩进的。原因 这是因为 Documents 节点是可扩展的(有子节点)。我在这里有它们来演示自动保存。
如果您想将所有 2 级节点移至左侧,只需将其移除即可。
ViewController.swift 代码
没什么好说的,除了 - 阅读 cmets :)
import Cocoa
// Sample Node class covering groups & regular items
class Node
let id: Int
let title: String
let symbolName: String?
let children: [Node]
let isGroup: Bool
init(id: Int, title: String, symbolName: String? = nil, children: [Node] = [], isGroup: Bool = false)
self.id = id
self.title = title
self.symbolName = symbolName
self.children = children
self.isGroup = isGroup
convenience init(groupId: Int, title: String, children: [Node])
self.init(id: groupId, title: title, children: children, isGroup: true)
extension Node
var cellIdentifier: NSUserInterfaceItemIdentifier
// These must match identifiers in Main.storyboard
NSUserInterfaceItemIdentifier(rawValue: isGroup ? "GroupCell" : "DataCell")
extension Array where Self.Element == Node
// Search for a node (recursively) until a matching element is found
func firstNode(where predicate: (Element) throws -> Bool) rethrows -> Element?
for element in self
if try predicate(element)
return element
if let matched = try element.children.firstNode(where: predicate)
return matched
return nil
class ViewController: NSViewController, NSOutlineViewDelegate, NSOutlineViewDataSource
@IBOutlet var outlineView: NSOutlineView!
let data = [
Node(groupId: 1, title: "Favorites", children: [
Node(id: 11, title: "AirDrop", symbolName: "wifi"),
Node(id: 12, title: "Recents", symbolName: "clock"),
Node(id: 13, title: "Applications", symbolName: "hammer")
]),
Node(groupId: 2, title: "iCloud", children: [
Node(id: 21, title: "iCloud Drive", symbolName: "icloud"),
Node(id: 22, title: "Documents", symbolName: "doc", children: [
Node(id: 221, title: "Work", symbolName: "folder"),
Node(id: 221, title: "Personal", symbolName: "folder.badge.person.crop"),
])
]),
]
override func viewWillAppear()
super.viewWillAppear()
// Expanded items are saved in the UserDefaults under the key:
//
// "NSOutlineView Items \(autosaveName)"
//
// By default, this value is not present. When you expand some nodes,
// an array with persistent objects is saved. When you collapse all nodes,
// the array is removed from the user defaults (not an empty array,
// but back to nil = removed).
//
// IOW there's no way to check if user already saw this source list,
// modified expansion state, etc. We will use custom key for this
// purpose, so we can expand group nodes (top level) when the source
// list is displayed for the first time.
//
// Next time, we wont expand anything and will honor autosaved expanded
// items.
if UserDefaults.standard.object(forKey: "FinderLikeSidebarAppeared") == nil
data.forEach
outlineView.expandItem($0)
UserDefaults.standard.set(true, forKey: "FinderLikeSidebarAppeared")
// Number of children or groups (item == nil)
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int
item == nil ? data.count : (item as! Node).children.count
// Child of a node or group (item == nil)
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any
item == nil ? data[index] : (item as! Node).children[index]
// View for our node
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView?
guard let node = item as? Node,
let cell = outlineView.makeView(withIdentifier: node.cellIdentifier, owner: self) as? NSTableCellView else
return nil
cell.textField?.stringValue = node.title
if !node.isGroup
cell.imageView?.image = NSImage(systemSymbolName: node.symbolName ?? "folder", accessibilityDescription: nil)
return cell
// Mark top level items as group items
func outlineView(_ outlineView: NSOutlineView, isGroupItem item: Any) -> Bool
(item as! Node).isGroup
// Every node is expandable if it has children
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool
!(item as! Node).children.isEmpty
// Top level items (group items) are not selectable
func outlineView(_ outlineView: NSOutlineView, shouldSelectItem item: Any) -> Bool
!(item as! Node).isGroup
// Object to save in the user defaults (NSOutlineView Items FinderLikeSidebar)
func outlineView(_ outlineView: NSOutlineView, persistentObjectForItem item: Any?) -> Any?
(item as! Node).id
// Find an item from the saved object (NSOutlineView Items FinderLikeSidebar)
func outlineView(_ outlineView: NSOutlineView, itemForPersistentObject object: Any) -> Any?
guard let id = object as? Int else return nil
return data.firstNode $0.id == id
【讨论】:
非常感谢您的详细实施。我希望实现扩展项目的自动保存。不幸的是,itemForPersistentObject 在我的数据从我的文件包装器加载之前被读取,并且 reloadData() 不会触发第二次检查,因此整个“自动扩展”位失败。以上是关于Big Sur 大纲视图可扩展项目损坏的主要内容,如果未能解决你的问题,请参考以下文章
word中一共有几种视图方式,并且如何区分几种视图关系,如:页面视图、大纲视图等等