使用Autolayout扩展NSTextViews

jem*_*ons 23 cocoa nstextview osx-lion autolayout

我的应用程序包含一个NSScrollView文档视图,其中包含一些垂直堆叠的文件NSTextViews- 每个文本视图在添加文本时都会在垂直方向上调整大小.

目前,这都是在代码中管理的.该NSTextViews自动调整,但我观察他们调整大小有NSViewFrameDidChangeNotification,重计算其所有来源,使他们不重叠,并调整其上海华盈(滚动视图的文档视图),使它们都适合和可滚动到.

这似乎是自动布局的完美候选人!我NSLayoutConstraints在第一个文本视图和它的容器,最后一个文本视图及其容器之间设置,以及彼此之间的每个文本视图.然后,如果任何文本视图增长,它会自动"按下"它下面的文本视图的来源以满足约束,最终增加文档视图的大小,每个人都很高兴!

除此之外,似乎没有办法NSTextView在基于约束的布局中添加文本时自动增长?使用与NSTextView之前输入的文本自动扩展完全相同的内容,如果我没有为其高度指定约束,则将其自动解析为0并且不显示.如果我确实指定了约束,即使是不等式(例如> = 20),它仍然会停留在该大小,并且不会随着文本的添加而增长.

我怀疑这与NSTextView的实现有关-intrinsicContentSize,默认情况下会返回(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric).

所以我的问题是:如果我根据我的文本布局NSTextView返回更有意义intrinsicContentSize的子类,那么我的自动布局会按预期工作吗?

关于实现intrinsicContentSize垂直调整大小NSTextView的任何指针?

Ale*_*ubo 26

我正在进行一个非常相似的设置 - 包含文本视图的垂直堆栈视图,这些视图可以扩展以适应其文本内容并使用自动布局.

到目前为止,我不得不进行子类化NSTextView,这不是很干净,但在实践中表现非常出色:

- (NSSize) intrinsicContentSize {
    NSTextContainer* textContainer = [self textContainer];
    NSLayoutManager* layoutManager = [self layoutManager];
    [layoutManager ensureLayoutForTextContainer: textContainer];
    return [layoutManager usedRectForTextContainer: textContainer].size;
}

- (void) didChangeText {
    [super didChangeText];
    [self invalidateIntrinsicContentSize];
}
Run Code Online (Sandbox Code Playgroud)

添加时文本视图的初始大小addSubview,奇怪的是,不是内在大小; 我还没弄明白如何发出第一次失效(挂钩viewDidMoveToSuperview没有帮助),但我相信我最终会弄明白.

  • 很有用!感谢您分享您的发现! (2认同)

mar*_*rux 12

我有一个与NSTextField类似的问题,结果发现它是由于视图想要沿垂直方向紧紧拥抱其文本内容.因此,如果您将内容拥抱优先级设置为低于其他约束的优先级,则可能会有效.例如:

[textView setContentHuggingPriority:NSLayoutPriorityFittingSizeCompression-1.0 forOrientation:NSLayoutConstraintOrientationVertical];
Run Code Online (Sandbox Code Playgroud)

在Swift中,这将是:

setContentHuggingPriority(NSLayoutConstraint.Priority.fittingSizeCompression, for:NSLayoutConstraint.Orientation.vertical)
Run Code Online (Sandbox Code Playgroud)


onm*_*133 6

以下是在Swift 3中使用自动布局制作扩展NSTextView的方法在此输入图像描述

  • 我使用Anchors进行自动布局
  • 使用textDidChangeNSTextDelegate.NSTextViewDelegate符合NSTextDelegate
  • 这个想法是textViewedges约束,这意味着每当它intrinsicContentSize发生变化时,它就会扩展它的父节点scrollView

    import Cocoa
    import Anchors
    
    class TextView: NSTextView {
      override var intrinsicContentSize: NSSize {
        guard let manager = textContainer?.layoutManager else {
          return .zero
        }
    
        manager.ensureLayout(for: textContainer!)
    
        return manager.usedRect(for: textContainer!).size
      }
    }
    
    class ViewController: NSViewController, NSTextViewDelegate {
    
      @IBOutlet var textView: NSTextView!
      @IBOutlet weak var scrollView: NSScrollView!
      override func viewDidLoad() {
        super.viewDidLoad()
    
        textView.delegate = self
    
        activate(
          scrollView.anchor.top.constant(100),
          scrollView.anchor.paddingHorizontally(30)
        )
    
        activate(
          textView.anchor.edges
        )
      }
    
      // MARK: - NSTextDelegate
      func textDidChange(_ notification: Notification) {
        guard let textView = notification.object as? NSTextView else { return }
    
        print(textView.intrinsicContentSize)
        textView.invalidateIntrinsicContentSize()
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)


Mat*_*pór 5

类已准备好复制和粘贴。斯威夫特 4.2、macOS 10.14

class HuggingTextView: NSTextView, NSTextViewDelegate {

    //MARK: - Initialization

    override init(frame: NSRect) {
        super.init(frame: frame)
        delegate = self
    }

    override init(frame frameRect: NSRect, textContainer container: NSTextContainer?) {
        super.init(frame: frameRect, textContainer: container)
        delegate = self
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        delegate = self
   }

    //MARK: - Overriden

    override var intrinsicContentSize: NSSize {
        guard let container = textContainer, let manager = container.layoutManager else {
            return super.intrinsicContentSize
        }
        manager.ensureLayout(for: container)
        return manager.usedRect(for: container).size
    }

    //MARK: - NSTextViewDelegate

    func textDidChange(_ notification: Notification) {
        invalidateIntrinsicContentSize()
    }

}
Run Code Online (Sandbox Code Playgroud)