Multiple views layouting in NSScrollView using Visual AutoLayout

浪尽此生 提交于 2019-12-02 04:18:21

问题


I need to show multiple views aligned vertically inside NSScrollView, I started by adding NSTableView and NSButton. I aligned them vertically with NSTableView on top and NSButton on bottom. I added NSTableview and NSButton to an NSView called tempView. And then set the document view of NSScrollView to tempView.

But problem i am having is my tableview does not expand properly i see the buttons alright but tablview does not expands properly and just show the last entries that fits in the table. As you can see in following image it displays last 4 rows of 20 rows.

] My code is as follow
-(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];
}

I am not getting the reason for such behavior.

[_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>
)

and _tableView intrinsicContentSize gives {-1, -1}.


回答1:


The problem seems to be that the table view has no intrinsic height. Therefore, it's not "pushing" against the document view's bounds to make the document view larger, so it scrolls.

I notice that if I add a table view to a window in IB and then tell IB to reset the whole window (or the clip view or table view) to suggested constraints, there are no constraints added between the table view and the clip view. Also, the table view still has translatesAutoresizingMaskIntoConstraints enabled. This is different from some other types of views in scroll views. So, I think that IB knows that table views aren't auto layout-savvy.

I believe that a table view directly in a clip view (as IB sets one up) just uses the old style mechanism for positioning and sizing itself. When a table view computes its size (e.g. a row is added), it probably just calls -setFrameSize: on itself. The clip view monitors its frame by observing NSViewFrameDidChangeNotification notification. This doesn't work in your case because auto layout basically ignores/undoes attempts to set the frame.

You may be able to work with this, but it will be dicey. You would leave the table view's translatesAutoresizingMaskIntoConstraints on, but that imposes limits on what constraints you can set without causing conflicts. Basically, you can't set constraints that impose a position or size on the table view, but you can "hang" other views off of the table view so long as they are free to move or resize so that the table view is free to move and resize.

First, your document view (tempView) should be a flipped view, because you're going to want to position the table view at its top by setting its frame and then it's going to resize itself by setting its frame size and you want it to grow down.

Init the document view with a frame with non-zero size. The origin doesn't matter.

Init the table view with a frame whose origin is at (0, 0) and with the same size as the document view. Don't forget to leave its translatesAutoresizingMaskIntoConstraints on.

Do not constrain the top, leading, or trailing edges of the table view to its superview. Don't constrain its width to match the button. The other constraints between the table view and its superview and button are fine, I think. So, kViewVertical should drop the first |. You should get rid of kTextViewHorizontal entirely. kButtonHorizontal is fine.

Set the table view's autoresizingMask to NSViewWidthSizable. Since the table view started with a frame coincident with its superview's bounds, that will keep the table view's left and right edges matching the superview's. There is no vertical component of the autoresizingMask. Basically, you don't want changes in the height of the superview to try to change the height of the table view. The table view is free to set its own frame height. That will move the button, because it's constrained to the table view's bottom, and that will change the height of the document view, because its bottom is constrained to the button's bottom.

Do not constrain the document view (tempView) to the clip view on its bottom edge. That just tries to force the document view to be the size of the contentSize of the scroll view. That is sure to prevent scrolling (by making the scroll view and the window grow to show the entire document view). Rather, the document view will change size as discussed in the previous paragraph and the clip view will notice that and update the scroll view accordingly.

I think that at this point, things will mostly work. I suspect that when the scroll view is large enough to show both the button and the whole table view, they will be pinned to the bottom of the scroll view rather than the top which would be more natural. To fix that, use a subclass of NSClipView that is flipped.



来源:https://stackoverflow.com/questions/29524577/multiple-views-layouting-in-nsscrollview-using-visual-autolayout

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!