【问题标题】:NSSplitView: Controlling divider position during window resizeNSSplitView:在窗口调整大小期间控制分隔线位置
【发布时间】:2012-02-27 20:38:29
【问题描述】:

我有一个 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 像素之间调整大小,并且在使用分隔手柄时可以正常工作。但是当调整整个窗口的大小时,分隔线会脱离这些限制。

有人知道如何控制吗?

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

提前致谢

阿恩

【问题讨论】:

    标签: cocoa nssplitview


    【解决方案1】:

    我最初实现了NSSplitView 委托函数,最后编写了大量代码来尝试做一些简单的事情,比如限制每个拆分视图边的最小尺寸。

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

    【讨论】:

    • 这是最好的方法。对于最大尺寸,为 NSView 上的宽度(
    【解决方案2】:

    你要找的是:

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

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

    【讨论】:

    • 对我来说,第一次调用 setFrame: 时不会调用它。但是,如果我将 NSSplitView 子类化并覆盖 resizeSubviewsWithOldSize,它确实会被调用。
    【解决方案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 上运行良好,并且比其他方法更简洁,因为它的代码最少且约束不重复。

    【讨论】:

    • 这也适用于我使用- (CGFloat)splitView:(NSSplitView *)splitView constrainSplitPosition:(CGFloat)proposedPosition ofSubviewAt:(NSInteger)dividerIndex 而不是最小/最大坐标。
    【解决方案4】:

    解决此问题的另一种方法是使用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;
    }
    

    【讨论】:

    • Ira,您的建议是正确的,可以轻松防止左侧拆分窗格(子视图)变得太小。但是,当调整窗口右边缘的大小时,一旦子视图达到最小值,左窗格将保持最小大小,并且不再与右侧窗格成比例增长。如果 shouldAdjustSizeOfSubview 传递了一个指标是增长还是缩小,那么该方法可能会完美运行。
    【解决方案5】:

    这是我对-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 没有最小值或最大值,并且不可折叠。

    【讨论】:

      【解决方案6】:

      我用过

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

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

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

      更改框架并没有给我一致的结果。设置了分隔线的位置。

      【讨论】:

        【解决方案7】:

        对于聚会来说可能为时已晚,但这是我对resizeSubviewWithOldSize: 的实现。在我的项目中,我需要一个垂直可调整大小的 NSplitView,leftView 宽度在 100.0 和 300.0 之间;没有考虑到“崩溃”。您应该注意子视图的所有可能维度。

        -(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);
            }
        }
        

        【讨论】:

          【解决方案8】:

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

          【讨论】:

            【解决方案9】:

            我只需要这样做,并想出了这个方法,它比前面的例子简单一点。此代码假定 NSSplitView 容器的左右 NSScrollView 有 IBOutlets,左右视图的最小尺寸有 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];
                }
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2012-08-13
              • 1970-01-01
              • 1970-01-01
              • 2014-12-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多