我试图让我的NSTextField的高度增长(就像在iChat或Adium中一样),一旦用户键入足够的文本以溢出控件的宽度(如on this post所示)
我已经接受了接受的答案,但我似乎无法让它发挥作用。我已将我的尝试上传到http://scottyob.com/pub/autoGrowingExample.zip
理想情况下,当文本增长时,包含的窗口应随之增长,但我正在尝试婴儿步骤。
答案 0 :(得分:17)
阅读Apple Documentation通常很有帮助。 Apple已经设计了所有这些文本布局的东西,足以处理各种complicated edge cases,这有时非常有用,有时甚至没有。
首先,我将文本字段设置为在分词上换行,所以我们实际上得到了多行。 (你的示例代码甚至有一个if语句,因此在关闭包装时它什么都没做。)
这一点的诀窍是要注意,正在编辑文本时,它是由“字段编辑器”打印的 - 一个重量级NSTextView
对象,由NSWindow
拥有,可以被任何东西重用NSTextField
目前是“第一响应者”(已选中)。 NSTextView
有一个NSTextContainer
(文字所在的矩形),其中有一个NSLayoutManager
来布局文本。我们可以向布局管理器询问它想要消耗多少空间,以获得文本字段的新高度。
另一个技巧是覆盖NSText
委托方法- (void)textDidChange:(NSNotification *)notification
,以便在文本更改时使内在内容大小无效(因此,当您通过按返回提交更改时,它不会等待更新)。
我之前没有使用cellSizeForBounds
的原因是我无法解决您的问题 - 即使在使单元格的内在内容大小无效时,cellSizeForBounds:
仍然会返回旧的大小
在GitHub上找到示例项目。
@interface TSTTextGrowth()
{
BOOL _hasLastIntrinsicSize;
BOOL _isEditing;
NSSize _lastIntrinsicSize;
}
@end
@implementation TSTTextGrowth
- (void)textDidBeginEditing:(NSNotification *)notification
{
[super textDidBeginEditing:notification];
_isEditing = YES;
}
- (void)textDidEndEditing:(NSNotification *)notification
{
[super textDidEndEditing:notification];
_isEditing = NO;
}
- (void)textDidChange:(NSNotification *)notification
{
[super textDidChange:notification];
[self invalidateIntrinsicContentSize];
}
-(NSSize)intrinsicContentSize
{
NSSize intrinsicSize = _lastIntrinsicSize;
// 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 || !_hasLastIntrinsicSize)
{
intrinsicSize = [super intrinsicContentSize];
// If we’re being edited, get the shared NSTextView field editor, so we can get more info
NSText *fieldEditor = [self.window fieldEditor:NO forObject:self];
if([fieldEditor isKindOfClass:[NSTextView class]])
{
NSTextView *textView = (NSTextView *)fieldEditor;
NSRect usedRect = [textView.textContainer.layoutManager usedRectForTextContainer:textView.textContainer];
usedRect.size.height += 5.0; // 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
intrinsicSize.height = usedRect.size.height;
}
_lastIntrinsicSize = intrinsicSize;
_hasLastIntrinsicSize = YES;
}
return intrinsicSize;
}
@end
作为最后一点,我自己从未真正使用过自动布局 - 这些演示看起来很神奇,但每当我亲自尝试时,我都无法让它工作得很好而且会让事情变得更复杂。但是,在这种情况下,我认为它实际上确实节省了大量工作 - 没有它,-intrinsicContentSize
将不存在,并且您可能必须自己设置框架,计算新原点以及新的大小(不是太难,但只是更多的代码)。
答案 1 :(得分:7)
DouglasHeriot的解决方案仅适用于固定宽度的文本字段。在我的应用程序中,我有文本字段,我想在水平和垂直方向上增长。因此我修改了解决方案如下:
<强> AutosizingTextField.h 强>
@interface AutosizingTextField : NSTextField {
BOOL isEditing;
}
@end
<强> AutosizingTextField.m 强>
@implementation AutosizingTextField
- (void)textDidBeginEditing:(NSNotification *)notification
{
[super textDidBeginEditing:notification];
isEditing = YES;
}
- (void)textDidEndEditing:(NSNotification *)notification
{
[super textDidEndEditing:notification];
isEditing = NO;
}
- (void)textDidChange:(NSNotification *)notification
{
[super textDidChange:notification];
[self invalidateIntrinsicContentSize];
}
-(NSSize)intrinsicContentSize
{
if(isEditing)
{
NSText *fieldEditor = [self.window fieldEditor:NO forObject:self];
if(fieldEditor)
{
NSTextFieldCell *cellCopy = [self.cell copy];
cellCopy.stringValue = fieldEditor.string;
return [cellCopy cellSize];
}
}
return [self.cell cellSize];
}
@end
还有一个小问题:当输入空格时,文本会向左跳一点。但是,这在我的应用中不是问题,因为在大多数情况下文本字段不应包含空格。
答案 2 :(得分:3)
DouglasHeriot的解决方案对我很有用。
这是Swift 4上的相同代码
class GrowingTextField: NSTextField {
var editing = false
var lastIntrinsicSize = NSSize.zero
var hasLastIntrinsicSize = false
override func textDidBeginEditing(_ notification: Notification) {
super.textDidBeginEditing(notification)
editing = true
}
override func textDidEndEditing(_ notification: Notification) {
super.textDidEndEditing(notification)
editing = false
}
override func textDidChange(_ notification: Notification) {
super.textDidChange(notification)
invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: NSSize {
get {
var intrinsicSize = lastIntrinsicSize
if editing || !hasLastIntrinsicSize {
intrinsicSize = super.intrinsicContentSize
// If we’re being edited, get the shared NSTextView field editor, so we can get more info
if let textView = self.window?.fieldEditor(false, for: self) as? NSTextView, let textContainer = textView.textContainer, var usedRect = textView.textContainer?.layoutManager?.usedRect(for: textContainer) {
usedRect.size.height += 5.0 // 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
intrinsicSize.height = usedRect.size.height
}
lastIntrinsicSize = intrinsicSize
hasLastIntrinsicSize = true
}
return intrinsicSize
}
}
}
答案 3 :(得分:0)
如果你想限制TextField的大小(例如):
if (intrinsicSize.height > 100)
{
intrinsicSize = _lastIntrinsicSize;
}
else
{
_lastIntrinsicSize = intrinsicSize;
_hasLastIntrinsicSize = YES;
}
(Diff)
我遇到的一件事就是将NSTextField嵌入到NSScrollView中并使其正常工作(特别是在NSStackView中)。要看看使用NSTextView是否会更容易。
答案 4 :(得分:0)
当设置文本字段的字符串值以及通过AutoLayout调整其大小时,此解决方案也适用。它仅在需要时使用属性化的text属性来计算内部内容的大小。
class AutoGrowingTextField: NSTextField {
var maximumHeight: CGFloat = 100
override var intrinsicContentSize: NSSize {
let height = attributedStringValue.boundingRect(
with: NSSize(width: bounds.width - 8, height: maximumHeight),
options: [NSString.DrawingOptions.usesLineFragmentOrigin]
).height + 5
return NSSize(width: NSView.noIntrinsicMetric, height: height)
}
override func textDidChange(_ notification: Notification) {
super.textDidChange(notification)
invalidateIntrinsicContentSize()
}
override func layout() {
super.layout()
invalidateIntrinsicContentSize()
}
}