使用 Visual AutoLayout 在 NSScrollView 中布局多个视图
Multiple views layouting in NSScrollView using Visual AutoLayout
我需要在 NSScrollView
内显示多个垂直对齐的视图,我首先添加了 NSTableView
和 NSButton
。我将它们垂直对齐,NSTableView
在顶部,NSButton
在底部。
我将 NSTableview
和 NSButton
添加到名为 tempView 的 NSView
。然后将NSScrollView
的文档视图设置为tempView.
但我遇到的问题是我的 table 视图没有正确展开我看到按钮没问题,但 tablview 没有正确展开并且只显示适合 table 的最后一个条目。
正如您在下图中看到的那样,它显示了 20 行中的最后 4 行。
]
我的代码如下
-(void)setupView {
_scrollView = [[NSScrollView alloc] init];
[_scrollView setHasVerticalScroller:YES];
[_scrollView setHasVerticalRuler:YES];
[_scrollView setBorderType:NSBezelBorder];
[_scrollView setBackgroundColor:[NSColor purpleColor]];
_scrollView.autoresizingMask = NSViewHeightSizable;
_tableView = [[NSTableView alloc] init];
[_tableView setDataSource:self];
[_tableView setHeaderView:nil];
[_tableView addTableColumn:[[NSTableColumn alloc] initWithIdentifier:@"firstColumn"]];
[_tableView setDelegate:self];
[_tableView setColumnAutoresizingStyle:NSTableViewUniformColumnAutoresizingStyle];
[_tableView setBackgroundColor:[NSColor greenColor]];
NSButton* _button = [[NSButton alloc] initWithFrame:NSZeroRect];
NSView* tempView = [[NSView alloc] initWithFrame:NSZeroRect];
[tempView setTranslatesAutoresizingMaskIntoConstraints:NO];
[_scrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
[_button setTranslatesAutoresizingMaskIntoConstraints:NO];
[_tableView setTranslatesAutoresizingMaskIntoConstraints:NO];
[tempView addSubview:_button];
[tempView addSubview:_tableView];
[_scrollView setDocumentView:tempView];
[self addSubview:_scrollView];
NSString *const kViewContainerVertical = @"V:|[tempView]|";
NSString *const kViewContainerHorizontal = @"H:|[tempView]|";
NSDictionary *viewDictionary = NSDictionaryOfVariableBindings(tempView);
NSArray *contraintOneView = [NSLayoutConstraint constraintsWithVisualFormat:kViewContainerVertical
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:viewDictionary];
NSArray *constraintTwoView = [NSLayoutConstraint constraintsWithVisualFormat:kViewContainerHorizontal
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:viewDictionary];
NSString *const kViewVertical = @"V:|[_tableView]-2-[_button]|";
NSString *const kTextViewHorizontal = @"H:|[_tableView(_button)]-0-|";
NSString *const kButtonHorizontal = @"H:|-0-[_button]-0-|";
NSDictionary *dictionary = NSDictionaryOfVariableBindings(_tableView, _button);
NSArray *contraintOne = [NSLayoutConstraint constraintsWithVisualFormat:kViewVertical
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:dictionary];
NSArray *constraintTwo = [NSLayoutConstraint constraintsWithVisualFormat:kTextViewHorizontal
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:dictionary];
NSArray *constraintThree = [NSLayoutConstraint constraintsWithVisualFormat:kButtonHorizontal
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:dictionary];
[tempView addConstraints:contraintOne];
[tempView addConstraints:constraintTwo];
[tempView addConstraints:constraintThree];
[_scrollView.contentView addConstraints:contraintOneView];
[_scrollView.contentView addConstraints:constraintTwoView];
NSString *const kScrollVertical = @"V:|-0-[_scrollView]-0-|";
NSString *const kScrollHorizontal = @"H:|-0-[_scrollView]-0-|";
NSDictionary *scrollDictionary = NSDictionaryOfVariableBindings(_scrollView);
NSArray *contraintOneScroll = [NSLayoutConstraint constraintsWithVisualFormat:kScrollVertical
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:scrollDictionary];
NSArray *constraintTwoScroll = [NSLayoutConstraint constraintsWithVisualFormat:kScrollHorizontal
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:scrollDictionary];
[self addConstraints:contraintOneScroll];
[self addConstraints:constraintTwoScroll];
}
我不明白这种行为的原因。
[_tableView constraintsAffectingLayoutForOrientation:NSLayoutConstraintOrientationVertical]
Returns
<__NSArrayI 0x608000101050>(
<NSContentSizeLayoutConstraint:0x6080000a2520 V:[NSButton:0x608000140c60'Button'(21)] Hug:250 CompressionResistance:750>,
<NSLayoutConstraint:0x608000082620 V:[NSButton:0x608000140c60'Button']-(0)-| (Names: '|':NSView:0x608000121180 )>,
<NSLayoutConstraint:0x6080000825d0 V:[NSTableView:0x1004032f0]-(2)-[NSButton:0x608000140c60'Button']>,
<NSLayoutConstraint:0x608000082da0 V:|-(0)-[JSFlippedView:0x608000160cc0] (Names: '|':NSView:0x610000120140 )>,
<NSAutoresizingMaskLayoutConstraint:0x608000081770 h=-&- v=-&- V:|-(1)-[NSClipView:0x100408c20] (Names: '|':NSScrollView:0x6080001c0000 )>,
<NSLayoutConstraint:0x608000082490 V:[NSView:0x608000121180]-(0)-| (Names: '|':NSClipView:0x100408c20 )>,
<NSLayoutConstraint:0x6080000828a0 V:[NSScrollView:0x6080001c0000]-(0)-| (Names: '|':JSFlippedView:0x608000160cc0 )>,
<NSAutoresizingMaskLayoutConstraint:0x608000082fd0 h=-&- v=-&- V:[NSView:0x610000120140]-(0)-| (Names: '|':NSThemeFrame:0x100403e30'ScrollTest-expand' )>,
<NSLayoutConstraint:0x608000082df0 V:[JSFlippedView:0x608000160cc0]-(0)-| (Names: '|':NSView:0x610000120140 )>,
<NSLayoutConstraint:0x608000082440 V:|-(0)-[NSView:0x608000121180] (Names: '|':NSClipView:0x100408c20 )>,
<NSAutoresizingMaskLayoutConstraint:0x608000081540 h=-&- v=-&- V:[NSClipView:0x100408c20]-(1)-| (Names: '|':NSScrollView:0x6080001c0000 )>,
<NSLayoutConstraint:0x608000082850 V:|-(0)-[NSScrollView:0x6080001c0000] (Names: '|':JSFlippedView:0x608000160cc0 )>,
<NSAutoresizingMaskLayoutConstraint:0x608000083200 h=-&- v=-&- V:|-(22)-[NSView:0x610000120140] (Names: '|':NSThemeFrame:0x100403e30'ScrollTest-expand' )>,
<NSLayoutConstraint:0x608000082580 V:|-(0)-[NSTableView:0x1004032f0] (Names: '|':NSView:0x608000121180 )>,
<NSLayoutConstraint:0x6000000806e0 'NSWindow-current-height' V:[NSThemeFrame:0x100403e30'ScrollTest-expand'(727@500)] priority:500>
)
和 _tableView intrinsicContentSize
给出 {-1, -1}
.
问题似乎是 table 视图没有固有高度。因此,使文档视图变大并不会 "pushing" 违反文档视图的边界,因此它会滚动。
我注意到,如果我在 IB 中将 table 视图添加到 window,然后告诉 IB 重置整个 window(或剪辑视图或 table 视图)到建议的约束,table 视图和剪辑视图之间没有添加任何约束。此外,table 视图仍然启用了 translatesAutoresizingMaskIntoConstraints
。这与滚动视图中的一些其他类型的视图不同。所以,我认为 IB 知道 table 视图不支持自动布局。
我相信 table 直接在剪辑视图中的视图(正如 IB 设置的那样)只是使用旧式机制来定位和调整自身大小。当 table 视图计算其大小时(例如添加一行),它可能只是调用 -setFrameSize:
自身。剪辑视图通过观察 NSViewFrameDidChangeNotification
通知来监视其帧。这在您的情况下不起作用,因为自动布局基本上 ignores/undoes 尝试设置框架。
您可能能够使用它,但这将是冒险的。您可以让 table 视图的 translatesAutoresizingMaskIntoConstraints
保持打开状态,但这会限制您可以设置哪些约束而不会引起冲突。基本上,您不能设置对 table 视图施加位置或大小的约束,但您可以 "hang" 其他视图离开 table 视图,只要它们可以自由移动或调整大小,以便 table 视图可以自由移动和调整大小。
首先,您的文档视图 (tempView
) 应该是翻转视图,因为您将要通过设置框架将 table 视图定位在其顶部,然后它会通过设置其帧大小来调整自身大小,并且您希望它变小。
使用非零大小的框架初始化文档视图。来源无所谓。
使用原点为 (0, 0) 且与文档视图大小相同的框架初始化 table 视图。不要忘记将其 translatesAutoresizingMaskIntoConstraints
保持打开状态。
不要将 table 视图的顶部、前导或尾部边缘约束到其父视图。不要限制其宽度以匹配按钮。 table 视图及其超级视图和按钮之间的其他约束很好,我认为。因此,kViewVertical
应该删除第一个 |
。你应该完全摆脱 kTextViewHorizontal
。 kButtonHorizontal
没问题。
将 table 视图的 autoresizingMask
设置为 NSViewWidthSizable
。由于 table 视图以与其父视图边界重合的框架开始,这将使 table 视图的左右边缘与父视图的匹配。 autoresizingMask
没有垂直分量。基本上,您不希望通过更改父视图的高度来尝试更改 table 视图的高度。 table 视图可以自由设置自己的框架高度。这将移动按钮,因为它被限制在 table 视图的底部,这将改变文档视图的高度,因为它的底部被限制在按钮的底部。
不要将文档视图 (tempView
) 限制到其底部边缘的剪辑视图。这只是试图强制文档视图成为滚动视图的 contentSize
的大小。这肯定会阻止滚动(通过使滚动视图和 window 增长以显示整个文档视图)。相反,文档视图将改变大小,如前一段所述,剪辑视图将注意到并相应地更新滚动视图。
我认为在这一点上,大部分事情都会奏效。我怀疑当滚动视图足够大以显示按钮和整个 table 视图时,它们将被固定到滚动视图的底部而不是顶部,这样会更自然。要解决此问题,请使用翻转的 NSClipView
的子类。
我需要在 NSScrollView
内显示多个垂直对齐的视图,我首先添加了 NSTableView
和 NSButton
。我将它们垂直对齐,NSTableView
在顶部,NSButton
在底部。
我将 NSTableview
和 NSButton
添加到名为 tempView 的 NSView
。然后将NSScrollView
的文档视图设置为tempView.
但我遇到的问题是我的 table 视图没有正确展开我看到按钮没问题,但 tablview 没有正确展开并且只显示适合 table 的最后一个条目。
正如您在下图中看到的那样,它显示了 20 行中的最后 4 行。
-(void)setupView {
_scrollView = [[NSScrollView alloc] init];
[_scrollView setHasVerticalScroller:YES];
[_scrollView setHasVerticalRuler:YES];
[_scrollView setBorderType:NSBezelBorder];
[_scrollView setBackgroundColor:[NSColor purpleColor]];
_scrollView.autoresizingMask = NSViewHeightSizable;
_tableView = [[NSTableView alloc] init];
[_tableView setDataSource:self];
[_tableView setHeaderView:nil];
[_tableView addTableColumn:[[NSTableColumn alloc] initWithIdentifier:@"firstColumn"]];
[_tableView setDelegate:self];
[_tableView setColumnAutoresizingStyle:NSTableViewUniformColumnAutoresizingStyle];
[_tableView setBackgroundColor:[NSColor greenColor]];
NSButton* _button = [[NSButton alloc] initWithFrame:NSZeroRect];
NSView* tempView = [[NSView alloc] initWithFrame:NSZeroRect];
[tempView setTranslatesAutoresizingMaskIntoConstraints:NO];
[_scrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
[_button setTranslatesAutoresizingMaskIntoConstraints:NO];
[_tableView setTranslatesAutoresizingMaskIntoConstraints:NO];
[tempView addSubview:_button];
[tempView addSubview:_tableView];
[_scrollView setDocumentView:tempView];
[self addSubview:_scrollView];
NSString *const kViewContainerVertical = @"V:|[tempView]|";
NSString *const kViewContainerHorizontal = @"H:|[tempView]|";
NSDictionary *viewDictionary = NSDictionaryOfVariableBindings(tempView);
NSArray *contraintOneView = [NSLayoutConstraint constraintsWithVisualFormat:kViewContainerVertical
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:viewDictionary];
NSArray *constraintTwoView = [NSLayoutConstraint constraintsWithVisualFormat:kViewContainerHorizontal
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:viewDictionary];
NSString *const kViewVertical = @"V:|[_tableView]-2-[_button]|";
NSString *const kTextViewHorizontal = @"H:|[_tableView(_button)]-0-|";
NSString *const kButtonHorizontal = @"H:|-0-[_button]-0-|";
NSDictionary *dictionary = NSDictionaryOfVariableBindings(_tableView, _button);
NSArray *contraintOne = [NSLayoutConstraint constraintsWithVisualFormat:kViewVertical
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:dictionary];
NSArray *constraintTwo = [NSLayoutConstraint constraintsWithVisualFormat:kTextViewHorizontal
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:dictionary];
NSArray *constraintThree = [NSLayoutConstraint constraintsWithVisualFormat:kButtonHorizontal
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:dictionary];
[tempView addConstraints:contraintOne];
[tempView addConstraints:constraintTwo];
[tempView addConstraints:constraintThree];
[_scrollView.contentView addConstraints:contraintOneView];
[_scrollView.contentView addConstraints:constraintTwoView];
NSString *const kScrollVertical = @"V:|-0-[_scrollView]-0-|";
NSString *const kScrollHorizontal = @"H:|-0-[_scrollView]-0-|";
NSDictionary *scrollDictionary = NSDictionaryOfVariableBindings(_scrollView);
NSArray *contraintOneScroll = [NSLayoutConstraint constraintsWithVisualFormat:kScrollVertical
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:scrollDictionary];
NSArray *constraintTwoScroll = [NSLayoutConstraint constraintsWithVisualFormat:kScrollHorizontal
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:scrollDictionary];
[self addConstraints:contraintOneScroll];
[self addConstraints:constraintTwoScroll];
}
我不明白这种行为的原因。
[_tableView constraintsAffectingLayoutForOrientation:NSLayoutConstraintOrientationVertical]
Returns
<__NSArrayI 0x608000101050>(
<NSContentSizeLayoutConstraint:0x6080000a2520 V:[NSButton:0x608000140c60'Button'(21)] Hug:250 CompressionResistance:750>,
<NSLayoutConstraint:0x608000082620 V:[NSButton:0x608000140c60'Button']-(0)-| (Names: '|':NSView:0x608000121180 )>,
<NSLayoutConstraint:0x6080000825d0 V:[NSTableView:0x1004032f0]-(2)-[NSButton:0x608000140c60'Button']>,
<NSLayoutConstraint:0x608000082da0 V:|-(0)-[JSFlippedView:0x608000160cc0] (Names: '|':NSView:0x610000120140 )>,
<NSAutoresizingMaskLayoutConstraint:0x608000081770 h=-&- v=-&- V:|-(1)-[NSClipView:0x100408c20] (Names: '|':NSScrollView:0x6080001c0000 )>,
<NSLayoutConstraint:0x608000082490 V:[NSView:0x608000121180]-(0)-| (Names: '|':NSClipView:0x100408c20 )>,
<NSLayoutConstraint:0x6080000828a0 V:[NSScrollView:0x6080001c0000]-(0)-| (Names: '|':JSFlippedView:0x608000160cc0 )>,
<NSAutoresizingMaskLayoutConstraint:0x608000082fd0 h=-&- v=-&- V:[NSView:0x610000120140]-(0)-| (Names: '|':NSThemeFrame:0x100403e30'ScrollTest-expand' )>,
<NSLayoutConstraint:0x608000082df0 V:[JSFlippedView:0x608000160cc0]-(0)-| (Names: '|':NSView:0x610000120140 )>,
<NSLayoutConstraint:0x608000082440 V:|-(0)-[NSView:0x608000121180] (Names: '|':NSClipView:0x100408c20 )>,
<NSAutoresizingMaskLayoutConstraint:0x608000081540 h=-&- v=-&- V:[NSClipView:0x100408c20]-(1)-| (Names: '|':NSScrollView:0x6080001c0000 )>,
<NSLayoutConstraint:0x608000082850 V:|-(0)-[NSScrollView:0x6080001c0000] (Names: '|':JSFlippedView:0x608000160cc0 )>,
<NSAutoresizingMaskLayoutConstraint:0x608000083200 h=-&- v=-&- V:|-(22)-[NSView:0x610000120140] (Names: '|':NSThemeFrame:0x100403e30'ScrollTest-expand' )>,
<NSLayoutConstraint:0x608000082580 V:|-(0)-[NSTableView:0x1004032f0] (Names: '|':NSView:0x608000121180 )>,
<NSLayoutConstraint:0x6000000806e0 'NSWindow-current-height' V:[NSThemeFrame:0x100403e30'ScrollTest-expand'(727@500)] priority:500>
)
和 _tableView intrinsicContentSize
给出 {-1, -1}
.
问题似乎是 table 视图没有固有高度。因此,使文档视图变大并不会 "pushing" 违反文档视图的边界,因此它会滚动。
我注意到,如果我在 IB 中将 table 视图添加到 window,然后告诉 IB 重置整个 window(或剪辑视图或 table 视图)到建议的约束,table 视图和剪辑视图之间没有添加任何约束。此外,table 视图仍然启用了 translatesAutoresizingMaskIntoConstraints
。这与滚动视图中的一些其他类型的视图不同。所以,我认为 IB 知道 table 视图不支持自动布局。
我相信 table 直接在剪辑视图中的视图(正如 IB 设置的那样)只是使用旧式机制来定位和调整自身大小。当 table 视图计算其大小时(例如添加一行),它可能只是调用 -setFrameSize:
自身。剪辑视图通过观察 NSViewFrameDidChangeNotification
通知来监视其帧。这在您的情况下不起作用,因为自动布局基本上 ignores/undoes 尝试设置框架。
您可能能够使用它,但这将是冒险的。您可以让 table 视图的 translatesAutoresizingMaskIntoConstraints
保持打开状态,但这会限制您可以设置哪些约束而不会引起冲突。基本上,您不能设置对 table 视图施加位置或大小的约束,但您可以 "hang" 其他视图离开 table 视图,只要它们可以自由移动或调整大小,以便 table 视图可以自由移动和调整大小。
首先,您的文档视图 (tempView
) 应该是翻转视图,因为您将要通过设置框架将 table 视图定位在其顶部,然后它会通过设置其帧大小来调整自身大小,并且您希望它变小。
使用非零大小的框架初始化文档视图。来源无所谓。
使用原点为 (0, 0) 且与文档视图大小相同的框架初始化 table 视图。不要忘记将其 translatesAutoresizingMaskIntoConstraints
保持打开状态。
不要将 table 视图的顶部、前导或尾部边缘约束到其父视图。不要限制其宽度以匹配按钮。 table 视图及其超级视图和按钮之间的其他约束很好,我认为。因此,kViewVertical
应该删除第一个 |
。你应该完全摆脱 kTextViewHorizontal
。 kButtonHorizontal
没问题。
将 table 视图的 autoresizingMask
设置为 NSViewWidthSizable
。由于 table 视图以与其父视图边界重合的框架开始,这将使 table 视图的左右边缘与父视图的匹配。 autoresizingMask
没有垂直分量。基本上,您不希望通过更改父视图的高度来尝试更改 table 视图的高度。 table 视图可以自由设置自己的框架高度。这将移动按钮,因为它被限制在 table 视图的底部,这将改变文档视图的高度,因为它的底部被限制在按钮的底部。
不要将文档视图 (tempView
) 限制到其底部边缘的剪辑视图。这只是试图强制文档视图成为滚动视图的 contentSize
的大小。这肯定会阻止滚动(通过使滚动视图和 window 增长以显示整个文档视图)。相反,文档视图将改变大小,如前一段所述,剪辑视图将注意到并相应地更新滚动视图。
我认为在这一点上,大部分事情都会奏效。我怀疑当滚动视图足够大以显示按钮和整个 table 视图时,它们将被固定到滚动视图的底部而不是顶部,这样会更自然。要解决此问题,请使用翻转的 NSClipView
的子类。