首页 > 解决方案 > NSTextList 格式化

问题描述

我试图弄清楚如何使用 NSTextList 但除了这个 SO questionthis blog 中的评论之外,在网上几乎没有找到有用的信息。

使用这个我已经能够创建一个粗略的例子来弄清楚:

    let textView = NSTextView()
    
    //Create NSTextList
    let list = NSTextList(markerFormat: .decimal, options: 0)
    list.startingItemNumber = 1
    
    //Create NSParagraphStyle with text list
    let paragraphStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
    paragraphStyle.textLists = [list]
    
    let attributes = [NSAttributedString.Key.paragraphStyle : paragraphStyle]
    
    //Add attributes to list block
    let nsmutableAttributedString = NSMutableAttributedString(string: "\t\(list.marker(forItemNumber: 1))\tList Item 1 ", attributes: attributes)
    
    textView.textStorage?.setAttributedString(nsmutableAttributedString)
    
    return textView

这很好用,当应用程序运行时,按下enter会创建一个格式正确的新行,按下会正确tab缩进列表项。

一切都很好......但是,标记周围的间距似乎不正确,并且不能反映您在 TextEdit 中得到的内容。

这是TextEdit,注意标记周围的间距 展示一个

这是我的 textView,请注意标记周围较大的间距,在下面突出显示的过程之后,它会恢复为“默认”文本编辑间距...

在此处输入图像描述

我希望摆脱这三件事:

  1. 我做错了吗?我很确定我必须\t在标记周围使用才能使其工作。
  2. 关于如何实现苹果在其所有文本编辑应用程序(笔记、文本编辑等)中实现的标记周围默认间距的任何想法
  3. NSTextList 的任何一般提示。那里没有太多东西,所以如果有人能分享他们的知识,将不胜感激。

标签: swiftmacosappkitnstextview

解决方案


注意事项:我也对此感到困惑,这是我尝试实施您上面链接的评论中的信息。

在我正在从事的项目中,我面临着类似的问题。发现 textView 正在更改 tabStops 数组 ( paragraphStyle.tabStops) 从默认值,当用户从嵌套列表移回主列表时,正如您在屏幕截图中所展示的那样。要进行验证,请尝试打印以控制第一个 tabStop ( paragraphStyle.tabStops.first.location) 的位置,用于保存“列表项 1”的段落与保存“列表继续”的段落。

我提出并仍在迭代的天真的解决方案是检查编辑的段落是否具有:

  • 一个文本列表
  • 一个 tabStop,其位置位于默认最左侧的 tabStop 位置的左侧。

如果上面的两个条件检查出来,我将已编辑段落的 tabStops 重置为默认值。

我目前正在研究以下功能的变体,专门用于解决您概述的问题。我调用了 didProcessEditing NSTextStorage 委托方法中的函数,传入了 textStorage 和editedRange。

    func handleTabStopsIssue(textStorage: NSTextStorage, editedRange: NSRange) {
        //Determine range of the edited paragraph (this is encompases the editedRange).
        let rangeOfEditedParagraph = textStorage.mutableString.paragraphRange(for: editedRange)
        //Retrieve the edited paragraph as attributed string
        let editedParagraph = textStorage.attributedSubstring(from: rangeOfEditedParagraph)
        //Enumerate the paragraphStyle attribute in the inputted paragraph NSAttributedString
        editedParagraph.enumerateAttribute(.paragraphStyle, in: NSRange(location: 0, length: editedParagraph.length), options: .longestEffectiveRangeNotRequired, using: {atts, subrange, _ in
            //Determine whether or not paragraph contains a textList
            guard let parStyle = atts as? NSParagraphStyle else { return }
            let parHasTextList = !parStyle.textLists.isEmpty
            
            //Retrieve the default tabStops to compare their location with inputted paragraph tabStop locations
            let defaultTabStops = NSParagraphStyle.default.tabStops
            guard let firstDefaultTabStop = defaultTabStops.first else { return }
            guard let inputParFirstTabStop = parStyle.tabStops.first else { return }
            print("firstDefaultTabStop \(firstDefaultTabStop) of location \(firstDefaultTabStop.location)")
            print("currentParTabStop \(inputParFirstTabStop) of location \(inputParFirstTabStop.location)")
            
            //If parStyle has textList, and its first tab stop is located to the left of the default tab stops...
            if (inputParFirstTabStop.location < firstDefaultTabStop.location) && parHasTextList {
                print(" Resetting tabstops!")
                //Set the inputted paragraph's tab stops, to default
                let newParStyle = parStyle.mutableCopy() as! NSMutableParagraphStyle
                newParStyle.tabStops = defaultTabStops
                textView.textStorage?.addAttribute(.paragraphStyle, value: newParStyle, range: rangeOfEditedParagraph)
            }
        })
    }

我在这里向 gitHub 上传了一个工作演示:https ://github.com/MQumairi/TextKit-List/blob/main/List%20Problems/ViewController.swift

将清晰度优先于效率,代码可能会更有效率。只是想展示我的逻辑。

这仍在进行中,因为上述方法不适用于阿拉伯语等从右到左的语言。需要处理那个边缘情况。但这是一个起点,希望可以帮助其他人解决这个问题。


推荐阅读