swift NSTableView的第一行最初出现在标题后面

axzmvihb  于 2023-06-28  发布在  Swift
关注(0)|答案(3)|浏览(104)

我在应用程序中以编程方式创建tableView,遇到了以下问题:
当我的tableView的内容大小大于其框架时,tableView的第一行最初出现在标题下,但可以轻松滚动回该位置。

    • 初始加载**后窗口的屏幕截图。

内容大小大于tableView框架(问题在这里):

内容大小小于tableView框架(这里都很好):

我的NSViewController类代码:

class MainViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {

let label: NSTextField = {
    let label = NSTextField()
    label.stringValue = "Test App"
    label.font = NSFont.boldSystemFont(ofSize: 14)
    label.textColor = .white
    label.alignment = .center
    label.drawsBackground = false
    label.isBezeled = false
    label.isSelectable = true
    label.isEditable = false
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}()

let labelContainerView: NSView = {
    let view = NSView()
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()

let tableView: NSTableView = {
    let tableView = NSTableView()
    tableView.translatesAutoresizingMaskIntoConstraints = false
    tableView.usesAlternatingRowBackgroundColors = true

    let column = NSTableColumn(identifier: .init(rawValue: "MyColumnID"))
    column.headerCell.title = "Test"
    column.headerCell.alignment = .center
    tableView.addTableColumn(column)

    return tableView
}()

let scrollView: NSScrollView = {
    let scrollView = NSScrollView()
    scrollView.hasVerticalScroller = true
    scrollView.translatesAutoresizingMaskIntoConstraints = false
    return scrollView
}()

override func loadView() {
    view = NSView()
    view.wantsLayer = true
}

var names: [String] = []

override func viewDidLoad() {
    super.viewDidLoad()

    layoutUI()

    for i in 1...9 {
        let name = "Test \(i)"
        names.append(name)
    }

    scrollView.documentView = tableView

    tableView.delegate = self
    tableView.dataSource = self
}


func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {

    let newCell = MyCellView(identfier: NSUserInterfaceItemIdentifier(rawValue: "MyColumnID"))

    newCell.textView.stringValue = names[row]

    return newCell
}

func numberOfRows(in tableView: NSTableView) -> Int {
    return names.count
}

func layoutUI() {
    view.addSubview(labelContainerView)
    view.addSubview(scrollView)
    labelContainerView.addSubview(label)

    NSLayoutConstraint.activate([
        labelContainerView.topAnchor.constraint(equalTo: view.topAnchor),
        labelContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        labelContainerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        labelContainerView.heightAnchor.constraint(equalTo: view.heightAnchor,
                                                   multiplier: 1/5),

        label.centerXAnchor.constraint(equalTo: labelContainerView.centerXAnchor),
        label.centerYAnchor.constraint(equalTo: labelContainerView.centerYAnchor,
                                       constant: -4),

        scrollView.heightAnchor.constraint(equalTo: view.heightAnchor,
                                           multiplier: 4/5),
        scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
}

}

1rhkuytd

1rhkuytd1#

我不建议在代码中设置NSTableView。在Interface Builder中执行此操作要容易得多,并且可以防止不必要的副作用。在任何情况下,如果NSTableView没有像预期的那样显示第一行,我会在相关的NSScrollView上使用scrollToVisible,以便在加载数据后使第一行可见。

nwsw7zdq

nwsw7zdq2#

我一直在解决同样的问题(以编程方式创建滚动表)。我构建了一个试验板版本,除了这个特殊的问题外,它可以正常工作。
当一个NSTableView有一个NSTableHeaderView时,NSScrollview实际上有两个NSClipview。从.contentView属性访问的第一个包含表视图,第二个包含标题和NSVisualEffectView。它们都是滚动视图的子视图。
通过调整contentView insets,我让它表现出来,即显示最上面的一行:

scrollView.contentView.contentInsets = NSEdgeInsets(top: headerView!.frame.height, left: 0, bottom: 0, right: 0)

我尝试了各种各样的自动布局策略,这些策略都不起作用,或者看起来很可疑。
下面是一个两列表的跟踪,其中填充了生成的数据,让我看看发生了什么。
概述(不是代码,但我可以让它格式化的唯一方法):

Scroll View Subviews: 4
    NSClipView
      NSTableBackgroundView (-250.0, -456.0, 1000.0, 456.0)
      NSTableView (0.0, 0.0, 500.0, 4750.0)
    NSClipView
      NSVisualEffectView (0.0, 0.0, 500.0, 23.0)
      NSTableHeaderView (0.0, 0.0, 500.0, 23.0)
    NSScroller
    NSScroller

两个剪辑视图及其框架矩形。0是内容视图:

ClipView 0 (0.0, 0.0, 500.0, 438.0)
      NSTableBackgroundView 0 (-250.0, -456.0, 1000.0, 456.0)
      NSTableView 1 (0.0, 0.0, 500.0, 4750.0)
    ClipView 1 (0.0, 0.0, 500.0, 23.0)
      NSVisualEffectView 0 (0.0, 0.0, 500.0, 23.0)
      NSTableHeaderView 1 (0.0, 0.0, 500.0, 23.0)

我还是不完全明白发生了什么。
但我确实相信,知道这些东西是如何在引擎盖下工作的,没有IB魔法,是一个值得的练习。

bakd9h0s

bakd9h0s3#

我尝试了罗恩的逻辑解决方案,或者在加载数据后使用'tableView.scrollToVisible(0)',但这个问题很烦人,我无法阻止第一行隐藏在headerView后面。相反,我不得不依靠AutoLayout替代方案来实现这一点,并同意罗恩关于学习以编程方式做事的评论。
基本上,由于NSScrollView的(“scrollView的”)documentView将管理scrollView在contentView内滚动的视图,因此在将scrollView的documentView设置为等于tableView之后,您可以(实际上)将scrollView的documentView(即tableView的可滚动部分)的topAnchor约束设置为低于tableView的headerView。
为此,将scrollView的documentView topAnchor约束设置为等于scrollView的topAnchor约束,并使用一个常量将tableView的headerView的高度添加到scrollView的topAnchor。这将把scrollView的documentView(显示tableView的可滚动部分)定位在tableView headerView之下(tableView headerView默认位于scrollView的顶部)。
请参见以下方法(包括“layoutScrollView”方法以供参考,该方法将在“layoutDocumentViewInScrollView”方法之前调用):

func layoutDocumentViewInScrollView() {
        tableView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.hasHorizontalScroller = false
        scrollView.hasVerticalScroller   = true

        scrollView.documentView = tableView
        
        NSLayoutConstraint.activate([
            scrollView.documentView!.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: tableView.headerView!.frame.height),
        ])
    }

    func layoutScrollView() {
        view.addSubView(scrollView)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0),
            scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0),
            scrollView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0),
            scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0),
        ])
    }

相关问题