如何使用显式 NSLayoutManager、NSTextStorage、NSTextContainer 以编程方式设置 NSTextView?

Posted

技术标签:

【中文标题】如何使用显式 NSLayoutManager、NSTextStorage、NSTextContainer 以编程方式设置 NSTextView?【英文标题】:How to set up an NSTextView programmatically with explicit NSLayoutManager, NSTextStorage, NSTextContainer? 【发布时间】:2015-03-13 14:55:45 【问题描述】:

按照苹果文档,我试图通过它的两个构造函数方法设置一个简单的NSTextView

我将下面的代码放在内容视图的视图控制器的viewDidAppear 方法中。 textView 是NSTextView 的实例,frameRect 是内容视图的框架。

以下 Swift 代码可以工作(给我一个可编辑的 textView 并在屏幕上显示文本):

    textView = NSTextView(frame: frameRect!)
    self.view.addSubview(textView)
    textView.textStorage?.appendAttributedString(NSAttributedString(string: "Hello"))

以下内容不起作用(文本视图不可编辑且屏幕上未显示文本):

    var textStorage = NSTextStorage()
    var layoutManager = NSLayoutManager()
    textStorage.addLayoutManager(layoutManager)
    var textContainer = NSTextContainer(containerSize: frameRect!.size)
    layoutManager.addTextContainer(textContainer)
    textView = NSTextView(frame: frameRect!, textContainer: textContainer)

    textView.editable = true
    textView.selectable = true
    self.view.addSubview(textView)

    textView.textStorage?.appendAttributedString(NSAttributedString(string: "Hello more complex"))

我在第二个示例中做错了什么?我正在尝试遵循 Apple 的“Cocoa Text Architecture Guide”中给出的示例,他们讨论了通过显式实例化其帮助对象网络来设置 NSTextView

【问题讨论】:

您是否保留了对textStorage 变量的引用? @PaulPatterson 现场!只是将 textStorage 的声明更改为类级别而不是本地级别,然后我就开始做生意了。请在下面弹出答案,以便我接受。我认为解释是局部变量在退出方法时被破坏,然后你有一个 textView,它有一个指向不存在的存储位置的指针? 没错。有趣的是,几个月前我花了大约一两个小时试图找出这个确切的问题 - 苹果应该在他们的指南中更明确一点。 【参考方案1】:

您需要保留对您创建的NSTextStorage 变量的引用。我不太确定这一切的机制,但看起来文本视图只保留对其文本存储对象的弱引用。一旦这个对象超出范围,它就不再可用于文本视图。我想这符合 MVC 设计模式,其中视图(在这种情况下为 NSTextView)意味着独立于它们的模型(NSTextStorage 对象)。

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate 

    @IBOutlet weak var window: NSWindow!

    var textView: NSTextView!
    var textStorage: NSTextStorage! // STORE A REFERENCE

    func applicationDidFinishLaunching(aNotification: NSNotification) 
        var view = window.contentView as NSView
        textStorage = NSTextStorage()
        var layoutManager = NSLayoutManager()
        textStorage.addLayoutManager(layoutManager)
        var textContainer = NSTextContainer(containerSize: view.bounds.size)
        layoutManager.addTextContainer(textContainer)
        textView = NSTextView(frame: view.bounds, textContainer: textContainer)

        textView.editable = true
        textView.selectable = true
        view.addSubview(textView)

        textView.textStorage?.appendAttributedString(NSAttributedString(string: "Hello more complex"))
    

【讨论】:

谢谢。关于最近让我发疯的另一件事:我在您的 AppDelegate 代码中看到一个 @IBOutlet 到您的主窗口......最近我发现 Xcode 不会让我的 AppDelegate 有窗口和视图的出口 -只有我吗?我控制拖动......什么都没有。 您使用的是 Storyboard 还是 Xibs? 我认为这就是问题所在。您只能为存在于同一场景中的对象创建出口。由于AppDelegate 位于应用程序场景中,我认为没有办法从视图控制器场景中的按钮创建一个出口。这个限制是我坚持使用 Xibs 的原因之一(我问题中的代码来自基于 Xib 的应用程序,因此是出口)。 感谢代码!请注意,您必须将textViewframe 设置为viewbounds,而不是其frame,因为textViewview 的子视图。如果view 本身是另一个视图的子视图,那么它的frame 将不在原点(0,0)并且textView 将被偏移并且可能甚至不可见。我会尝试编辑您的答案以反映这一点。 添加我的 2cents:无法弄清楚为什么我在以编程方式添加文本视图时看不到或输入任何文本。问题是 textview 框架必须匹配或小于要添加的视图(框架)【参考方案2】:

在 Xcode 12.4 下测试。在游乐场:

import Cocoa
import AppKit

let textViewFrame = CGRect(x: 0, y: 0, width: 250, height: 90)
let textStorage = NSTextStorage()
var layoutManager = NSLayoutManager()
textStorage.addLayoutManager(layoutManager)
var textContainer = NSTextContainer(containerSize: textViewFrame.size)
layoutManager.addTextContainer(textContainer)
let textView = NSTextView(frame: textViewFrame, textContainer: textContainer)
textView.isEditable = true
textView.isSelectable = true
textView.textColor = NSColor.red
textView.string = "Why is this so complicated..."

【讨论】:

【参考方案3】:
#import <Cocoa/Cocoa.h>

@interface TextViewController : NSObject 

    NSLayoutManager *secondLayout;

    IBOutlet NSSplitView *columnView;
    IBOutlet NSTextView *bottomView;



- (IBAction) addColumn: (id)sender;

@end
#import "TextViewController.h"

@implementation TextViewController

- (void)awakeFromNib

    NSTextStorage *storage = [bottomView textStorage];
    secondLayout = [NSLayoutManager new];
    [storage addLayoutManager: secondLayout];
    [secondLayout release];
    [self addColumn: nil];
    [self addColumn: nil];



- (IBAction) addColumn: (id)sender

    NSRect frame = [columnView frame];

    NSTextContainer *container = [[NSTextContainer alloc]
                                  initWithContainerSize: frame.size];
    [container setHeightTracksTextView: YES];
    [container setWidthTracksTextView: YES];

    [secondLayout addTextContainer: container];
    [container release];
    NSTextView *newView = [[NSTextView alloc] initWithFrame: frame
                                              textContainer: container];
    [columnView addSubview: newView];
    [newView release];


@end

【讨论】:

这段代码是做什么的,你能添加一些描述它与问题的关系吗?

以上是关于如何使用显式 NSLayoutManager、NSTextStorage、NSTextContainer 以编程方式设置 NSTextView?的主要内容,如果未能解决你的问题,请参考以下文章

TypeORM:如何在没有加载关系的属性的情况下显式设置 ForeignKey?

如何使用 Text Kit 查找单行文本的高度

如何使用预定义的 NS 创建 route53 区域,或更新注册域的 NS?

如何使用新的 iOS 7 API 获取自动换行信息?

如何使用 datetime64[ns] 和 pandas 计算天数?

如何使用块来处理 NS 方法返回的错误