1

我正在尝试为 Mac OS 制作带有格式的文本编辑器。我一直在使用NSTextView一个自定义NSTextStorage类。它将粗体等属性应用于NSAttributableStrings.

这一切似乎都很好,如下面的屏幕截图所示。这是一个附加NSTextView的自定义NSTextStorage类。它通过属性应用格式NSAttributeableString

NSTextView 没有滚动和自定义 TextStorage 用于格式化

然而,一切都一样,但是从 Apple 提供的函数中获取一个可滚动的 NSTextView ,NSTextView.scrollableTextView()它根本不显示任何文本。即使您可以在屏幕截图中看到NStextView它实际上是可见的。此外,将鼠标移到编辑器上会将光标更改为编辑器光标。但我不能选择、输入或做任何事情。

带有客户 NSTextStorage 的可滚动文本视图,不显示任何内容

做与上面完全相同的事情,但不向文本视图提供文本容器确实表明它已正确连接,因为我确实得到了一个可滚动的文本视图。滚动实际起作用的地方,但当然不再应用格式。

NSTextView 滚动工作,但不应用格式

所以我很困惑我现在必须做什么。

这基本上是我的设置:

//
//  TextDocumentViewController.swift
//
//  Created by Matthijn on 15/02/2022.
//  Based on LayoutWithTextKit2 Sample from Apple

import Foundation
import AppKit

class TextDocumentViewController: NSViewController {
    
    // Extends NSTextStorage, applies attributes to NSAttributeAbleString for formatting
    private var textStorage: TextStorage

    // Not custom yet, default implementation - do I need to subclass this specifically and implement something to support the scrolling behaviour? Which seems weird to me since it does work without scrolling support 
    private var layoutManager: NSLayoutManager
    // Also default implementation
    private var textContainer: NSTextContainer
    
    private var textDocumentView: NSTextView
    private var scrollView: NSScrollView

    required init(content: String) {
        textStorage = TextStorage(editorAttributes: MarkdownAttributes)


        layoutManager = NSLayoutManager()
        textStorage.addLayoutManager(layoutManager)
        
        
        textContainer = NSTextContainer()
        // I'm not 100% sure if I need this on false or true or can just do defaults. No combination fixes it
        // textContainer.heightTracksTextView = false
        // textContainer.widthTracksTextView = true
        
        layoutManager.addTextContainer(textContainer)
        
        scrollView = NSTextView.scrollableTextView()
        textDocumentView = (scrollView.documentView as! NSTextView)
    
        // Basically commenting this out, stops applying my NSTextContainer, NSLayoutManager and NSTextContainer, but then of course the formatting is not applied. This one line changes it between it works without formatting, or it doesn't work at all. (Unless I have my text view not embedded in a scroll view) then it works but the scrolling of course then does not work.       
        textDocumentView.textContainer = textContainer
    
        textDocumentView.string = content
        textDocumentView.isVerticallyResizable = true
        textDocumentView.isHorizontallyResizable = false
        
        super.init(nibName: nil, bundle: nil)
    }
    
    override func loadView() {
        view = scrollView
    }
}
4

1 回答 1

1

您实际上可以使用创建NSScrollView实例,scrollableTextView()并且可以获得隐式创建的documentView (NSTextView).

最后,可以将现有LayoutManager的分配给继承自documentView的自己的类。TextStorageNSTextStorage

然后,viewDidLoad您可以scrollView使用addSubview:. 对于 AutoLayout,一如既往,translatesAutoresizingMaskIntoConstraints必须设置为false.

使用widthAnchor和 agreaterThanOrEqualToConstant您可以定义最小尺寸,因此用户不能将其周围的窗口缩小。该结构还允许以后使用附加的粘性视图(例如面包屑视图等)进行潜在的简单扩展。

代码

如果你以这种方式实现它,那么对于一个小的最小测试它可能看起来像这样。

import Cocoa


class ViewController: NSViewController {

    private var scrollView: NSScrollView
    private var textDocumentView: NSTextView

    required init(content: String) {
        scrollView = NSTextView.scrollableTextView()
        let textDocumentView = scrollView.documentView as! NSTextView
        self.textDocumentView = textDocumentView
        let textStorage = TextStorage(editorAttributes: MarkdownAttributes())
        textStorage.addLayoutManager(textDocumentView.layoutManager!)

        textDocumentView.string = content

        super.init(nibName: nil, bundle: nil)

    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(scrollView)
        NSLayoutConstraint.activate([
            scrollView.topAnchor.constraint(equalTo: view.topAnchor),
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.widthAnchor.constraint(greaterThanOrEqualToConstant: 200.0),
            scrollView.heightAnchor.constraint(greaterThanOrEqualToConstant: 200.0),
        ])
    }
    
    override func loadView() {
      self.view = NSView()
    }
    
}

测试

这是一个快速测试,显示显示和滚动都按预期工作:

演示

于 2022-03-03T10:33:35.413 回答