You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI macOS应用中NSTableView无法显示问题求助

SwiftUI macOS中NSTableView无法正常显示的问题

我是Swift新手,正在开发SwiftUI macOS(≥12.2)应用,尝试将NSTableView作为NSView的子视图添加,但表格视图无法在屏幕上显示。

更新记录

  • 更新1:
    • 文末已添加最小可复现示例
    • 已根据@jnpdx的建议更新代码
    • 注意:取消注释configureTableView()中的//addSubview(tableView)行(即重复调用addSubview(tableView)),可在屏幕最底部看到一个窄表格。
  • 更新2:似乎需要保留两处addSubview(tableView)调用,才能让tableView成为CustomView的层级子视图并在其范围内显示,且不会创建新实例。目前问题变为表格位于应用视图底部(应在顶部)及确保文本框可见。

FilesView类实现代码

class FilesView: NSView {
    let files: Files
    @Published var selectedFile: AudioFile? = nil
    @Published var sortDescending = false
    var tableView = NSTableView()
    var nameColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "name"))
    var statusColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "status"))
    var dateColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "date"))
    var groupingColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "grouping"))
    var durationColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "duration"))
    init(files:Files){
        self.files = files
        super.init(frame: .zero)
        configureTableView()
    }
    func addFile(file: AudioFile) {
        self.files.files.append(file)
        tableView.reloadData()
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) 尚未实现")
    }   
    func configureTableView() {
        print("进入 configureTableView()")
        tableView.delegate = self
        tableView.dataSource = self
        tableView.identifier = NSUserInterfaceItemIdentifier(rawValue: "cell")
        tableView.addTableColumn(nameColumn)
        tableView.addTableColumn(statusColumn)
        tableView.addTableColumn(dateColumn)
        tableView.addTableColumn(groupingColumn)
        tableView.addTableColumn(durationColumn)
        tableView.headerView = nil
        tableView.backgroundColor = .black
        tableView.usesAlternatingRowBackgroundColors = true
        tableView.gridStyleMask = .solidVerticalGridLineMask
        tableView.focusRingType = .none
        tableView.selectionHighlightStyle = .none
        tableView.rowSizeStyle = .default
        tableView.reloadData()
        //addSubview(tableView) // tableView 是 FilesView 的子视图,而 FilesView 是 CustomView 的子视图,当创建 CustomView 并传入 tableView 时,tableView 会被添加为 CustomView 的子视图。
    }
}

CustomView类实现代码

class CustomView: NSView {
    private let tableView: FilesView
    private let textView: TextBox

    init(tableView: FilesView, textView: TextBox) {
        self.tableView = tableView
        self.textView = textView
        super.init(frame: .zero)
        addSubview(tableView)
        addSubview(textView)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) 尚未实现")
    }

    override func layout() {
        super.layout()
        let tableViewHeight = frame.height * 0.7
        let textViewHeight = frame.height * 0.3
        tableView.frame = NSRect(x: 0, y: 0, width: frame.width, height: tableViewHeight)
        textView.frame = NSRect(x: 0, y: tableViewHeight, width: frame.width, height: textViewHeight)
    }

}

ContentView中调用CustomView的代码

struct CustomViewRepresentable: NSViewRepresentable {
    let filesView = FilesView(files: Files())
    let textBox = TextBox()

    func makeNSView(context: Context) -> NSView {
        let customView = CustomView(tableView: filesView, textView: textBox)
        return customView
    }

    func updateNSView(_ nsView: NSView, context: Context) {
        // 如有必要,更新NSView
    }

    typealias NSViewType = NSView
}

ContentView的实现

private var customViewRepresentable: CustomViewRepresentable

init(){
    self.customViewRepresentable = CustomViewRepresentable()
}

ContentView的body部分

var body: some View {
    
        VStack {
            customViewRepresentable
        }
}

最小可复现示例

import SwiftUI
import Foundation
import Combine

class FilesView: NSView {
    //let files: Files
    @Published var sortDescending = false
    var tableView = NSTableView()
    var nameColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "name"))
    var statusColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "status"))
    var dateColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "date"))
    var groupingColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "grouping"))
    var durationColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "duration"))
    init(){
        super.init(frame: .zero)
        configureTableView()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) 尚未实现")
    }
    func configureTableView() {
        print("进入 configureTableView()")
        tableView.delegate = self
        tableView.dataSource = self
        tableView.identifier = NSUserInterfaceItemIdentifier(rawValue: "cell")
        tableView.addTableColumn(nameColumn)
        tableView.addTableColumn(statusColumn)
        tableView.addTableColumn(dateColumn)
        tableView.addTableColumn(groupingColumn)
        tableView.addTableColumn(durationColumn)
        tableView.headerView = nil
        tableView.backgroundColor = .black
        tableView.usesAlternatingRowBackgroundColors = true
        tableView.gridStyleMask = .solidVerticalGridLineMask
        tableView.focusRingType = .none
        tableView.selectionHighlightStyle = .none
        tableView.rowSizeStyle = .default
        tableView.reloadData()
        //addSubview(tableView) // tableView 是 FilesView 的子视图,而 FilesView 是 CustomView 的子视图,当创建 CustomView 并传入 tableView 时,tableView 会被添加为 CustomView 的子视图。
    }
}

extension FilesView: NSTableViewDataSource {
    func numberOfRows(in tableView: NSTableView) -> Int {
        return 0
    }
}

extension FilesView: NSTableViewDelegate {
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        guard let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "cell"), owner: nil) as? NSTableCellView else {
            return nil
        }
        if tableColumn == tableView.tableColumns[0] {
            cell.textField?.stringValue = ""
        } else if tableColumn == tableView.tableColumns[1] {
            cell.textField?.stringValue = ""
        }
        else if tableColumn == tableView.tableColumns[2] {
            cell.textField?.stringValue = ""
        }
        else if tableColumn == tableView.tableColumns[3] {
            cell.textField?.stringValue = ""
        }
        else if tableColumn == tableView.tableColumns[4]{
            cell.textField?.stringValue = ""
        }
        return cell
    }
}

class TextBox: NSView {
    @State private var selectedOption = 0 // 0 = 删除线, 1 = 隐藏, 2 = 下划线
    @State private var text = ""
    @State private var isTextBoxHidden = false
    
    var textEditor: some View {
        TextEditor(text: $text)
            .frame(minHeight: 200)
            .padding()
            .overlay(
                RoundedRectangle(cornerRadius: 10)
                    .stroke(Color.gray, lineWidth: 2)
            )
    }

    var exportButton: some View {
        HStack {
            Spacer()
            Button(action: {
                // 导出代码
            }) {
                Text("导出")
            }
            Spacer()
        }
    }
    
    override func layout() {
        super.layout()
        // 布局视图
    }
}

class CustomView: NSView {
    private let tableView: FilesView
    private let textView: TextBox

    init(tableView: FilesView, textView: TextBox) {
        self.tableView = tableView
        self.textView = textView
        super.init(frame: .zero)
        addSubview(tableView)
        addSubview(textView)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) 尚未实现")
    }

    override func layout() {
        super.layout()
        let tableViewHeight = frame.height * 0.7
        let textViewHeight = frame.height * 0.3
        tableView.frame = NSRect(x: 0, y: 0, width: frame.width, height: tableViewHeight)
        textView.frame = NSRect(x: 0, y: tableViewHeight, width: frame.width, height: textViewHeight)
    }

}

struct CustomViewRepresentable: NSViewRepresentable {
    let filesView = FilesView()
    let textBox = TextBox()

    func makeNSView(context: Context) -> NSView {
        let customView = CustomView(tableView: filesView, textView: textBox)
        return customView
    }

    func updateNSView(_ nsView: NSView, context: Context) {
        // 如有必要,更新NSView
    }

    typealias NSViewType = NSView
}

//let customViewRepresentable = CustomViewRepresentable()

//    init(){
//        self.customViewRepresentable = CustomViewRepresentable()
//    }

struct ContentView: View {

    private var customViewRepresentable: CustomViewRepresentable

    init(){
        self.customViewRepresentable = CustomViewRepresentable()
    }

    // 遵循ObservableObject协议
    var didChange = PassthroughSubject<Void, Never>()

    var body: some View {

            VStack {
                customViewRepresentable
            }
        }
    }

内容的提问来源于stack exchange,提问作者ballade4op52

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 15:15:10