I have a NSTextField
that I try to resize automatically given a certain criteria whenever the content of it changes.
Sometimes, when start typing the content start moving up (or down) the visible part of the text field as shown in the following gif:
If i click inside the NSTextField
, the content appears in the right position again.
Firing up the visual debugger in XCode I saw that when this case happen, the private subview of NSTextField
: _NSKeyboardFocusClipView
has a frame
whose Y
coordinate has a negative number.
I am not sure what causes that.
Here is my textField resize behavior:
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)"
)
}
}
which is invoked on the NSTextFieldDelegate
method:
extension CanvasViewController: NSTextFieldDelegate {
override func controlTextDidChange(_ obj: Notification) {
if let textField = obj.object as? NSTextField {
textFieldResizingBehavior.resize(textField)
}
}
And finally my textfield is declared in the viewController like:
lazy var textField: NSTextField = {
let textField = NSTextField()
textField.isHidden = true
textField.isEditable = false
textField.allowsEditingTextAttributes = true
return textField
}()
full code in: https://github.com/fespinoza/linked-ideas-osx
One part of the problem is that you are directly invoking the becomeFirstResponder
method. You should not do this.
According to the documentation:
Use the NSWindow makeFirstResponder(_:) method, not this method, to make an object the first responder. Never invoke this method directly.
Furthermore, do you really need the textfield to be able to grow both horizontally and vertically? Making it dynamic based on height alone would obviously be much more straight-forward.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With