NSSplitView:在窗口调整大小期间控制分隔符位置

时间:2012-02-27 20:38:29

标签: cocoa nssplitview

我有一个NSSplitView,它有两个窗格 - 左边是侧边栏表视图,右边是一个Web视图。我还有一个委托集,处理侧边栏的约束,如下所示:

- (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex {
    return 500.0f;
}

- (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
    return 175.0f;
}

- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview {
    return NO;
}

这意味着侧边栏只能在175到500像素之间调整大小,这在使用分隔符句柄时工作正常。但是当调整整个窗口的大小时,分隔符会从这些约束中重新定位。

有人知道如何控制这个吗?

此外:如果我想存储用户选择的侧边栏宽度,是否可以将其读出,将其保存到首选项文件并稍后恢复,或者是否有更直接的方法来执行此操作?我注意到窗口的状态在某些情况下会被保存 - 这通常会发生还是我必须控制它?

提前致谢

·阿尔

9 个答案:

答案 0 :(得分:4)

您正在寻找的是:

- (void)splitView:(NSSplitView*)sender resizeSubviewsWithOldSize:(NSSize)oldSize
调整大小后,

[sender frame]将是NSSplitView的新大小。然后相应地重新调整您的子视图。

答案 1 :(得分:3)

问题是,当调整NSSplitView本身的大小时,会调用-adjustSubviews来完成工作,但它会忽略委托中的最小/最大约束!

-setPosition:ofDividerAtIndex:确实考虑了约束。

你需要做的就是将两者结合起来 - 这个例子假设NSSplitView只有2个视图:

- (CGFloat)splitView:(NSSplitView*)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
  return 300;
}

- (CGFloat)splitView:(NSSplitView*)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex {
  return (splitView.vertical ? splitView.bounds.size.width : splitView.bounds.size.height) - 500;
}

- (void)splitView:(NSSplitView*)splitView resizeSubviewsWithOldSize:(NSSize)oldSize {
  [splitView adjustSubviews];  // Use default resizing behavior from NSSplitView
  NSView* view = splitView.subviews.firstObject;
  [splitView setPosition:(splitView.vertical ? view.frame.size.width : view.frame.size.height) ofDividerAtIndex:0];  // Force-apply constraints afterwards
}

这似乎在OS X 10.8,10.9和10.10上运行良好,并且比其他方法更清晰,因为它的代码最小且约束不重复。

答案 2 :(得分:3)

我最初实现了NSSplitView委托函数,并最终使用了大量代码来尝试做一些简单的操作,例如限制每个拆分视图边的最小大小。

然后我改变了我的方法,找到了一个干净且非常简单的解决方案。我只是为NSView的一侧NSSplitView上的宽度(> =我想要的最小尺寸)设置了一个自动布局常量。我在另一边做了同样的事情。通过这两个简单约束,NSSplitView可以完美地工作,而无需委托调用。

答案 3 :(得分:2)

另一种解决方法是使用splitView:shouldAdjustSizeOfSubview:

我发现这对我来说更简单。

例如,如果你想阻止sidebarTableView小于你的175最小宽度,那么你可以做这样的事情(假设你在视图控制器/委托上做了sidebarTableView一个插座);

- (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)subview
{
    if ((subview==self.sidebarTableView) && subview.bounds.size.width<=175) {
        return NO;
    }
    return YES;
}

答案 4 :(得分:1)

以下是我-splitView:resizeSubviewsWithOldSize:的实现:

-(void)splitView:(NSSplitView *)splitView resizeSubviewsWithOldSize:(NSSize)oldSize {
    if (![splitView isSubviewCollapsed:self.rightView] &&
        self.rightView.frame.size.width < 275.0f + DBL_EPSILON) {
        NSSize splitViewFrameSize = splitView.frame.size;
        CGFloat leftViewWidth = splitViewFrameSize.width - 275.0f - splitView.dividerThickness;
        self.leftView.frameSize = NSMakeSize(leftViewWidth,
                                             splitViewFrameSize.height);
        self.rightView.frame = NSMakeRect(leftViewWidth + splitView.dividerThickness,
                                          0.0f,
                                          275.0,
                                          splitViewFrameSize.height);
    } else
        [splitView adjustSubviews];
}

就我而言,rightView是两个子视图中的第二个,它是可折叠的,最小宽度为275.0。 leftView没有最小值或最大值,并且不可折叠。

答案 5 :(得分:0)

我用过

- (void)splitView:(NSSplitView*)sender resizeSubviewsWithOldSize:(NSSize)oldSize

但是我没有改变子视图框架,而是使用了

    [sender setPosition:360 ofDividerAtIndex:0]; //or whatever your index and position should be

更改框架并没有给我一致的结果。设置分隔符的位置确实如此。

答案 6 :(得分:0)

对于派对来说可能为时已晚,但这是我对resizeSubviewWithOldSize:的实施。在我的项目中,我需要一个垂直可调整大小的NSplitView,其leftView宽度在100.0和300.0之间;没有&#39;倒塌&#39;考虑到了。您应该处理子视图的所有可能维度。

-(void)splitView:(NSSplitView *)splitView resizeSubviewsWithOldSize:(NSSize)oldSize {
    if (self.leftView.frame.size.width >= kMaxLeftWidth) {
        NSSize splitViewFrameSize = splitView.frame.size;
        CGFloat leftViewWidth = kMaxLeftWidth;
        CGFloat rightViewWidth = splitViewFrameSize.width - leftViewWidth - splitView.dividerThickness;

        self.leftView.frameSize = NSMakeSize(leftViewWidth,
                                             splitViewFrameSize.height);

        self.rightView.frame = NSMakeRect(leftViewWidth + splitView.dividerThickness,
                                          0.0f,
                                          rightViewWidth,
                                          splitViewFrameSize.height);
    } else if (self.leftView.frame.size.width <= kMinLeftWidth) {
        NSSize splitViewFrameSize = splitView.frame.size;
        CGFloat leftViewWidth = kMinLeftWidth;
        CGFloat rightViewWidth = splitViewFrameSize.width - leftViewWidth - splitView.dividerThickness;

        self.leftView.frameSize = NSMakeSize(leftViewWidth,
                                             splitViewFrameSize.height);

        self.rightView.frame = NSMakeRect(leftViewWidth + splitView.dividerThickness,
                                          0.0f,
                                          rightViewWidth,
                                          splitViewFrameSize.height);
    } else {
        NSSize splitViewFrameSize = splitView.frame.size;
        CGFloat leftViewWidth = self.leftView.frame.size.width;
        CGFloat rightViewWidth = splitViewFrameSize.width - leftViewWidth - splitView.dividerThickness;

        self.leftView.frameSize = NSMakeSize(leftViewWidth,
                                             splitViewFrameSize.height);

        self.rightView.frame = NSMakeRect(leftViewWidth + splitView.dividerThickness,
                                          0.0f,
                                          rightViewWidth,
                                          splitViewFrameSize.height);
    }
}

答案 7 :(得分:0)

我通过在Interface Builder中将固定端的holdingPriority NSSplitViewItem设置为Required(1000)来实现此行为。然后,您可以通过在基础NSView上设置约束来控制固定边的宽度。

答案 8 :(得分:0)

我只需要这样做,并想出了这个比以前的例子简单一点的方法。此代码假定NSSplitView容器的左侧和右侧NSScrollViews有IBOutlet,左侧和右侧视图的最小大小为CGFloat常量。

#pragma mark - NSSplitView sizing override

//------------------------------------------------------------------------------
// This is implemented to ensure that when the window is resized that our main table
// remains at it's smallest size or larger (the default proportional sizing done by
// -adjustSubviews would size it smaller w/o -constrainMinCoordiante being called).

- (void) splitView: (NSSplitView *)inSplitView resizeSubviewsWithOldSize: (NSSize)oldSize
{
    // First, let the default proportional adjustments take place
    [inSplitView adjustSubviews];

    // Then ensure that our views are at least their min size
    // *** WARNING: this does not handle allowing the window to be made smaller than the total of the two views!

    // Gather current sizes
    NSSize leftViewSize = self.leftSideView.frame.size;
    NSSize rightViewSize = self.rightSideView.frame.size;
    NSSize splitViewSize = inSplitView.frame.size;
    CGFloat dividerWidth = inSplitView.dividerThickness;

    // Assume we don't have to resize anything
    CGFloat newLeftWidth = 0.0f;

    // Always adjust the left view first if we need to change either view's size
    if( leftViewSize.width < kLeftSplitViewMinSize )
    {
        newLeftWidth = kLeftSplitViewMinSize;
    }
    else if( rightViewSize.width < kRightSplitViewMinSize )
    {
        newLeftWidth = splitViewSize.width - (kRightSplitViewMinSize + dividerWidth);
    }

    // Do we need to adjust the size?
    if( newLeftWidth > 0.0f )
    {
        // Yes, do so by setting the left view and setting the right view to the space left over
        leftViewSize.width = newLeftWidth;
        rightViewSize.width = splitViewSize.width - (newLeftWidth + dividerWidth);

        // We also need to set the origin of the right view correctly
        NSPoint origin = self.rightSideView.frame.origin;
        origin.x = splitViewSize.width - rightViewSize.width;
        [self.rightSideView setFrameOrigin: origin];

        // Set the the ajusted view sizes
        leftViewSize.height = rightViewSize.height = splitViewSize.height;
        [self.leftSideView setFrameSize: leftViewSize];
        [self.rightSideView setFrameSize: rightViewSize];
    }
}