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

如何在NSTextView的NSTextTable中添加嵌套表格?

问题:NSTextTableBlock嵌套表格实现失败

我基于NSTextTableBlock创建了表格,尝试添加嵌套表格但未成功。此前用HTML尝试相同操作时,CSS表格样式仅对首个属性生效。

初始实现代码

@IBOutlet var textView: NSTextView!

override func viewDidLoad() {
    super.viewDidLoad()
        
    let table = getTableString()
    textView.textStorage?.setAttributedString(table)
}

func getTableString() -> NSMutableAttributedString {
    var table = NSTextTable()
    table.numberOfColumns = 4
    var mas = NSMutableAttributedString()

    // Header
    mas.append(createTableBlock(string: "Header. 1", textTable: table, row: 0, column: 0, width: 25, isHeader: true))
    mas.append(createTableBlock(string: "Header. 2", textTable: table, row: 0, column: 1, width: 25, isHeader: true))
    mas.append(createTableBlock(string: "Header. 3", textTable: table, row: 0, column: 2, width: 25, isHeader: true))
    mas.append(createTableBlock(string: "Header. 4", textTable: table, row: 0, column: 3, width: 25, isHeader: true))
    
    // Row 1
    mas.append(createTableBlock(string: "Nr. 1", textTable: table, row: 1, column: 0, width: 25))
    mas.append(createTableBlock(string: "Nr. 2", textTable: table, row: 1, column: 1, width: 25))
    mas.append(createTableBlock(string: "-mark-", textTable: table, row: 1, column: 2, width: 50))
    
    // Create the nested table
    var nestedTable = NSTextTable()
    nestedTable.numberOfColumns = 2
    var nestedMAS = NSMutableAttributedString()
    
    nestedMAS.append(createTableBlock(string: "Nested 1", textTable: nestedTable, row: 0, column: 0, width: 24, isNested: true))
    nestedMAS.append(createTableBlock(string: "Nested 2", textTable: nestedTable, row: 0, column: 1, width: 24, isNested: true))
    
    let needleRange = mas.string.range(of: "-mark-")!
    let insertionRange = NSRange(needleRange, in: mas.string)
    mas.replaceCharacters(in: insertionRange, with: nestedMAS)
    
    return mas
}

func createTableBlock(string: String, textTable: NSTextTable, row: Int, column: Int, width: CGFloat, isHeader: Bool = false, isNested: Bool = false) -> NSMutableAttributedString {
    var offset = 1
    if isHeader || isNested {
        offset = 1
    } else if column >= 2 {
        offset = 2
    }
    
    let block = NSTextTableBlock(table: textTable, startingRow: row, rowSpan: 1, startingColumn: column, columnSpan: offset)
    
    block.backgroundColor = isHeader ? .lightGray : .white
    block.setBorderColor(isNested ? .red : .green)
    block.setWidth(1.0, type: NSTextBlock.ValueType.absoluteValueType, for: NSTextBlock.Layer.border)
    block.setContentWidth(width, type: .percentageValueType)
    
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.alignment = isNested ? .center : .left
    paragraphStyle.textBlocks = [block]
    
    var mas = NSMutableAttributedString(string: string + "\n")
    mas.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, mas.length))
    
    return mas
}

效果对比

  • 预期效果:
    预期效果
  • 实际运行结果:
    实际效果

更新后的问题

已成功在单元格中插入字符串,但无法将其设置为双列单元格形式,更新后的代码如下:

@IBOutlet var textView: NSTextView!


override func viewDidLoad() {
    super.viewDidLoad()

    let table = getTableString()!
    textView.textStorage?.setAttributedString(table)
}

func getTableString() -> NSMutableAttributedString? {
    let table = NSTextTable()
    table.numberOfColumns = 4
    let mas: NSMutableAttributedString = NSMutableAttributedString()

    // Header
    mas.append(addCell(string: "Header. 1\n", textTable: table, row: 0, column: 0, width: 25, isHeader: true))
    mas.append(addCell(string: "Header. 2\n", textTable: table, row: 0, column: 1, width: 25, isHeader: true))
    mas.append(addCell(string: "Header. 3\n", textTable: table, row: 0, column: 2, width: 25, isHeader: true))
    mas.append(addCell(string: "Header. 4\n", textTable: table, row: 0, column: 3, width: 25, isHeader: true))
    
    // Row 1
    mas.append(addCell(string: "Nr. 1\n", textTable: table, row: 1, column: 0, width: 25))
    mas.append(addCell(string: "Nr. 2\n", textTable: table, row: 1, column: 1, width: 25))
    
    // Composed nested table
    mas.append(addNestedCell(textTable: table, row: 1, column: 2, width: 50))

    return mas
}

// Regular cell
func addCell(string:String, textTable: NSTextTable, row:Int, column:Int, columnSpan:Int=1, width:CGFloat, isHeader:Bool=false, isNested:Bool=false) -> NSMutableAttributedString {
    let block = createTextTableBlock(table: textTable, row: row, column: column, columnSpan: columnSpan, width: width, isHeader: isHeader, isNested: isNested)
    
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.alignment = isNested ? .center : .left
    paragraphStyle.textBlocks = [block]
    
    let mas = NSMutableAttributedString(string: string)
    mas.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, mas.length))
    
    return mas
}

// Nested cell
func addNestedCell(textTable: NSTextTable, row: Int, column: Int, width: CGFloat) -> NSMutableAttributedString {
    let parentBlock = createTextTableBlock(table: textTable, row: row, column: column, columnSpan: 2, width: width)
    
    // Create the nested table
    let nestedTable = NSTextTable()
    nestedTable.numberOfColumns = 2
    
    let nestedMAS = NSMutableAttributedString()
    
    // Nested cell 1
    nestedMAS.append(addCell(string: "Nested 1\n", textTable: nestedTable, row: 0, column: 0, width: 25, isNested: true))
    
    // Nested cell 2
    nestedMAS.append(addCell(string: "Nested 2\n", textTable: nestedTable, row: 0, column: 1, width: 25, isNested: true))
    
    let paraNestStyle = getParagraphStyle(nestedMAS)
    let textNestBlocks = paraNestStyle!.textBlocks

    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.alignment = .center
    paragraphStyle.textBlocks = [parentBlock]
    paragraphStyle.textBlocks.append(contentsOf: textNestBlocks)
    
    nestedMAS.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, nestedMAS.length))
    
    return nestedMAS
}

// TableBlock builder
func createTextTableBlock(table textTable:NSTextTable, row:Int, column:Int, columnSpan:Int=1, width:CGFloat, isHeader:Bool=false, isNested:Bool=false) -> NSTextTableBlock {
    let block = NSTextTableBlock(table: textTable, startingRow: row, rowSpan: 1, startingColumn: column, columnSpan: columnSpan)
    
    block.backgroundColor = isHeader ? .lightGray : .white
    block.setBorderColor(isNested ? .red : .green)
    block.setWidth(1.0, type: NSTextBlock.ValueType.absoluteValueType, for: NSTextBlock.Layer.border)
    block.setContentWidth(width, type: .percentageValueType)
    
    return block
}

// Get paragraph style from NSMutableAttributedString
func getParagraphStyle(_ attributedString:NSMutableAttributedString, at index:Int=0) -> NSMutableParagraphStyle? {
    let range = NSMakeRange(0, attributedString.length)
    guard let paraStyle = attributedString.attribute(NSAttributedString.Key.paragraphStyle, at:index, longestEffectiveRange:nil, in:range) as? NSMutableParagraphStyle else { return nil }
    return paraStyle
}

当前显示效果

当前效果

恳请各位提供实现嵌套表格的可行思路或解决方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 13:07:02