Использование Autolayout с расширением NSTextViews

Мое приложение состоит из NSScrollView, представление документа которого содержит несколько вертикально расположенных NSTextViews - каждый из которых изменяется в вертикальном направлении по мере добавления текста.

В настоящее время все это управляется кодом. NSTextViews изменяет размер автоматически, но я наблюдаю их изменение размера с помощью NSViewFrameDidChangeNotification, recalc все их происхождение, чтобы они не перекрывались и изменяли размер своего супервизора (вид документа с прокруткой), чтобы они все соответствовали и могут быть прокручены к.

Кажется, что это был бы идеальный кандидат на автозапуск! Я установил NSLayoutConstraints между первым текстовым видом и его контейнером, последним текстовым видом и его контейнером, а также каждым текстовым видом между собой. Затем, если какой-либо текстовый вид растет, он автоматически "толкает" истоки текстовых представлений под ним, чтобы удовлетворить противоречия, в конечном счете увеличивая размер представления документа, и все счастливы!

Кроме того, кажется, что нет способа сделать NSTextView автоматически растущим, поскольку текст добавляется в макете с ограничениями? Используя тот же самый NSTextView, который автоматически расширялся по мере ввода текста до этого, если я не укажу ограничение для его высоты, он имеет значение 0 и не отображается. Если я задаю ограничение, даже такое неравенство, как >= 20, оно остается застрявшим в этом размере и не растет с добавлением текста.

Я подозреваю, что это связано с реализацией NSTextView -intrinsicContentSize, которая по умолчанию возвращает (NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric).

Итак, мои вопросы: если я подклассифицирую NSTextView для возврата более значимого intrinsicContentSize на основе макета моего текста, будет ли мой автозапуск работать как ожидалось?

Любые указатели на реализацию intrinsicContentSize для вертикального изменения размера NSTextView?

Ответ 1

У меня была аналогичная проблема с NSTextField, и оказалось, что это было из-за представления, которое нужно было обнимать его текстовое содержимое по вертикальной ориентации. Поэтому, если вы установите приоритет обхода контента на что-то ниже приоритетов других ограничений, это может сработать. Например:.

[textView setContentHuggingPriority:NSLayoutPriorityFittingSizeCompression-1.0 forOrientation:NSLayoutConstraintOrientationVertical];

Ответ 2

Я работаю над очень похожими настройками - вертикальный стек представлений, содержащий текстовые представления, которые расширяются, чтобы соответствовать их содержимому текста и использовать автозапуск.

До сих пор мне приходилось подклассом 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];
}

Исходный размер текстового представления при добавлении addSubview, с любопытством, не является внутренним размером; Я еще не понял, как выдать первое недействительное (привязка viewDidMoveToSuperview не помогает), но я уверен, что в конце концов это выясню.

Ответ 3

Вот как сделать расширяющийся NSTextView с помощью Auto Layout, в Swift 3 введите описание изображения здесь

  • Я использовал Anchors для автоматической компоновки
  • Используйте textDidChange из NSTextDelegate. NSTextViewDelegate соответствует NSTextDelegate
  • Идея состоит в том, что textView имеет ограничения edges, что означает, что всякий раз, когда изменяется его 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()
      }
    }