Странная прокрутка на NSTextField

У меня есть NSTextField, который я пытаюсь автоматически изменять размер, учитывая определенные критерии, когда изменяется его содержимое.

Иногда, когда вы начинаете вводить текст, начинайте перемещать (или вниз) видимую часть текстового поля, как показано в следующем файле gif:

gif

Если я нажму внутри NSTextField, содержимое снова появится в правильной позиции.

Увольнение визуального отладчика в XCode я увидел, что когда этот случай случается, у частного поднабора NSTextField: _NSKeyboardFocusClipView есть frame, чья координата Y имеет отрицательное число.

Я не уверен, что вызывает это.

Вот мой способ изменения размера textField:

import Cocoa

struct TextFieldResizingBehavior {
  let maxHeight: CGFloat = 100000.0
  let maxWidthPadding: CGFloat = 10
  let minWidth: CGFloat = 50
  let maxWidth: CGFloat = 250

  func resize(_ textField: NSTextField) {
    let originalFrame = textField.frame

    var textMaxWidth = textField.attributedStringValue.size().width
    textMaxWidth = textMaxWidth > maxWidth ? maxWidth : textMaxWidth
    textMaxWidth += maxWidthPadding

    var constraintBounds: NSRect = textField.frame
    constraintBounds.size.width = textMaxWidth
    constraintBounds.size.height = maxHeight

    var naturalSize = textField.cell!.cellSize(forBounds: constraintBounds)

    // ensure minimun size of text field
    naturalSize.width = naturalSize.width < minWidth ? minWidth : naturalSize.width

    if originalFrame.height != naturalSize.height {
      // correct the origin in order the textField to grow down.
      let yOffset: CGFloat = naturalSize.height - originalFrame.height
      let newOrigin = NSPoint(
        x: originalFrame.origin.x,
        y: originalFrame.origin.y - yOffset
      )
      textField.setFrameOrigin(newOrigin)
    }

    textField.setFrameSize(naturalSize)

    Swift.print(
      "\n\n>>>>>> text field resized " +
        "\nnaturalSize=\(naturalSize)" +
        "\noriginalFrame=\(originalFrame)-\(originalFrame.center)" +
        "\nnewFrame=\(textField.frame)-\(textField.frame.center)"
    )
  }
}

который вызывается по методу NSTextFieldDelegate:

extension CanvasViewController: NSTextFieldDelegate {
  override func controlTextDidChange(_ obj: Notification) {
    if let textField = obj.object as? NSTextField {
      textFieldResizingBehavior.resize(textField)
    }
  }

И, наконец, мое текстовое поле объявляется в viewController как:

lazy var textField: NSTextField = {
    let textField = NSTextField()
    textField.isHidden = true
    textField.isEditable = false
    textField.allowsEditingTextAttributes = true
    return textField
  }()

полный код в: https://github.com/fespinoza/linked-ideas-osx

Ответ 1

Одна из проблем заключается в том, что вы непосредственно вызываете метод becomeFirstResponder. Вы не должны этого делать.

В соответствии с документация:

Используйте метод NSWindow makeFirstResponder (_:), а не этот метод, чтобы сделать объект первым ответчиком. Никогда не вызывайте этот метод напрямую.

Кроме того, вам действительно нужно, чтобы текстовое поле могло расти как по горизонтали, так и по вертикали? Очевидно, что динамика, основанная только на высоте, будет намного более прямой.