Xcode 9.1, Swift 4
I'm trying to create an NSTextField
that grows in height as the user enters text. Just like the one in iMessage on a Mac. Here's an example video: http://d.pr/v/zWRA6w
I have set up the NSViews
like this so that I can put a custom design around the NSTextField
and just leave its default border and background off:
Here are my constraints. The chat conversation scrolls underneath the chat wrap.
I tried to follow this answer and created the following Swift version:
class ResizingTextField: NSTextField{
var isEditing = false
override func textDidBeginEditing(_ notification: Notification) {
super.textDidBeginEditing(notification)
isEditing = true
}
override func textDidEndEditing(_ notification: Notification) {
super.textDidEndEditing(notification)
isEditing = false
}
override func textDidChange(_ notification: Notification) {
super.textDidChange(notification)
self.invalidateIntrinsicContentSize()
}
override public var intrinsicContentSize: CGSize {
if isEditing{
let fieldEditor = self.window?.fieldEditor(false, for: self)
if fieldEditor != nil{
if let cellCopy = self.cell?.copy() as? NSTextFieldCell{
cellCopy.stringValue = fieldEditor!.string
return cellCopy.cellSize
}
}
}
return self.cell!.cellSize
}
}
But there must be something wrong with my constraints and/or code, as nothing happens when I type in the box.
Any suggestions?
I came across this which worked: https://gist.github.com/entotsu/ddc136832a87a0fd2f9a0a6d4cf754ea
I had to update the code a bit to work with Swift 4:
class AutoGrowingTextField: NSTextField {
var minHeight: CGFloat? = 22
let bottomSpace: CGFloat = 7
// magic number! (the field editor TextView is offset within the NSTextField. It’s easy to get the space above (it’s origin), but it’s difficult to get the default spacing for the bottom, as we may be changing the height
var heightLimit: CGFloat?
var lastSize: NSSize?
var isEditing = false
override func textDidBeginEditing(_ notification: Notification) {
super.textDidBeginEditing(notification)
isEditing = true
}
override func textDidEndEditing(_ notification: Notification) {
super.textDidEndEditing(notification)
isEditing = false
}
override func textDidChange(_ notification: Notification) {
super.textDidChange(notification)
self.invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: NSSize {
var minSize: NSSize {
var size = super.intrinsicContentSize
size.height = minHeight ?? 0
return size
}
// Only update the size if we’re editing the text, or if we’ve not set it yet
// If we try and update it while another text field is selected, it may shrink back down to only the size of one line (for some reason?)
if isEditing || lastSize == nil {
//If we’re being edited, get the shared NSTextView field editor, so we can get more info
guard let textView = self.window?.fieldEditor(false, for: self) as? NSTextView, let container = textView.textContainer, let newHeight = container.layoutManager?.usedRect(for: container).height
else {
return lastSize ?? minSize
}
var newSize = super.intrinsicContentSize
newSize.height = newHeight + bottomSpace
if let heightLimit = heightLimit, let lastSize = lastSize, newSize.height > heightLimit {
newSize = lastSize
}
if let minHeight = minHeight, newSize.height < minHeight {
newSize.height = minHeight
}
lastSize = newSize
return newSize
}
else {
return lastSize ?? minSize
}
}
}
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