带有自定义 NSTextStorage 的可滚动 NSTextView 用于格式化
Scrollable NSTextView with custom NSTextStorage for formatting
我正在尝试制作一个具有 Mac OS 格式的文本编辑器。我正在使用 NSTextView
和自定义 NSTextStorage
class。它将 bold 等属性应用于 NSAttributableStrings
.
这一切似乎工作正常,如下面的屏幕截图所示。这是一个带有自定义 NSTextStorage
class 的 NSTextView
。通过 NSAttributeableString
上的属性应用格式
然而,一切都一样,但是从 Apple 提供的函数 NSTextView.scrollableTextView()
获得可滚动的 NSTextView 它根本不显示任何文本。尽管您可以在屏幕截图中看到 NStextView
实际上是可见的。此外,将鼠标移到编辑器上会将光标更改为编辑器光标。但我无法 select、打字或做任何事情。
做与上面完全相同的事情,但不向文本视图提供文本容器确实表明它已正确连接,因为我确实得到了一个可滚动的文本视图。滚动实际有效的地方,但当然不再应用格式。
所以我对我现在必须做的事情感到困惑。
这基本上是我的设置:
//
// 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
}
}
您实际上可以使用 scrollableTextView()
创建 NSScrollView
实例,您可以获得隐式创建的 documentView (NSTextView)
.
最后,可以将 documentView
的现有 LayoutManager
分配给继承自 NSTextStorage
.
的 TextStorage
class
在 viewDidLoad
中,您可以使用 addSubview:
将 scrollView
添加到视图中。对于 AutoLayout,一如既往,translatesAutoresizingMaskIntoConstraints
必须设置为 false
。
使用 widthAnchor
和 greaterThanOrEqualToConstant
可以定义最小尺寸,因此用户无法将其周围的 window 变小。该结构还允许以后使用附加的粘性视图(例如面包屑视图等)进行潜在的简单扩展。
代码
如果您以这种方式实现它,那么对于一个小的最小测试,它可能看起来像这样。
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()
}
}
测试
这是一个快速测试,显示显示和滚动都按设置正常工作:
我正在尝试制作一个具有 Mac OS 格式的文本编辑器。我正在使用 NSTextView
和自定义 NSTextStorage
class。它将 bold 等属性应用于 NSAttributableStrings
.
这一切似乎工作正常,如下面的屏幕截图所示。这是一个带有自定义 NSTextStorage
class 的 NSTextView
。通过 NSAttributeableString
然而,一切都一样,但是从 Apple 提供的函数 NSTextView.scrollableTextView()
获得可滚动的 NSTextView 它根本不显示任何文本。尽管您可以在屏幕截图中看到 NStextView
实际上是可见的。此外,将鼠标移到编辑器上会将光标更改为编辑器光标。但我无法 select、打字或做任何事情。
做与上面完全相同的事情,但不向文本视图提供文本容器确实表明它已正确连接,因为我确实得到了一个可滚动的文本视图。滚动实际有效的地方,但当然不再应用格式。
所以我对我现在必须做的事情感到困惑。
这基本上是我的设置:
//
// 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
}
}
您实际上可以使用 scrollableTextView()
创建 NSScrollView
实例,您可以获得隐式创建的 documentView (NSTextView)
.
最后,可以将 documentView
的现有 LayoutManager
分配给继承自 NSTextStorage
.
TextStorage
class
在 viewDidLoad
中,您可以使用 addSubview:
将 scrollView
添加到视图中。对于 AutoLayout,一如既往,translatesAutoresizingMaskIntoConstraints
必须设置为 false
。
使用 widthAnchor
和 greaterThanOrEqualToConstant
可以定义最小尺寸,因此用户无法将其周围的 window 变小。该结构还允许以后使用附加的粘性视图(例如面包屑视图等)进行潜在的简单扩展。
代码
如果您以这种方式实现它,那么对于一个小的最小测试,它可能看起来像这样。
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()
}
}
测试
这是一个快速测试,显示显示和滚动都按设置正常工作: