解决了! (灵感来自https://github.com/jerrykrinock/CategoriesObjC/blob/master/NS(Attributed)String%2BGeometrics/NS(Attributed)String%2BGeometrics.m)
阅读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 将不存在,您可能必须自己设置框架,计算新原点以及新尺寸(不是太难,只是更多的代码)。