【问题标题】:How to expand and collapse parts of NSSplitView programmatically?如何以编程方式展开和折叠部分 NSSplitView?
【发布时间】:2010-10-29 19:53:30
【问题描述】:

我想在现有项目中将RBSplitView 替换为NSSplitView。该应用程序现在只有 leopard,我想将 RBSplitView 替换为 Leopard 附带的 NSSplitView

但是,我在NSSplitView 中缺少RBSplitView 的便捷方法expandcollapse。如何以编程方式展开和折叠部分 NSSplitView?

【问题讨论】:

    标签: cocoa expand nssplitview


    【解决方案1】:

    只需隐藏要折叠的子视图,例如

    [aSubViewToCollapse setHidden:YES];
    

    您可能还想实现委托方法 -(BOOL)splitView:shouldHideDividerAtIndex: 返回 YES 以在折叠时隐藏分隔线。

    【讨论】:

    • +1 这应该是我预期的实际工作的答案。如果子视图被隐藏,-[NSSplitView isSubviewCollapsed:] 返回YES
    • 无论如何你需要在设置隐藏后立即调用-[NSSplitView adjustSubviews]更新图形。
    • 这个方法有效,但是当我再次将 hidden 设置为 NO 时,它具有使我隐藏的视图变小的奇怪的副作用
    • 要解决@Will 所指的奇怪副作用(隐藏视图变窄),请在视图取消隐藏后显式设置分隔线位置。
    • 我刚刚在 OS X 10.11 上测试了这个解决方案,它显示了我预期的结果:拆分视图的子视图被隐藏,布局或分隔位置没有任何变化。所以这不是问题的解决方案。
    【解决方案2】:

    我刚刚得到了NSSplitView 的程序化扩展和折叠功能。我还配置了我的NSSplitView 以在双击分隔线时展开/折叠子视图,所以我希望这个功能与该功能配合得很好(而且似乎如此)。这就是我所做的:

    (在本例中,splitViewNSSplitView 本身,splitViewSubViewLeft 是我希望展开/折叠的子视图,lastSplitViewSubViewLeftWidthCGFloat 类型的实例变量。)

    // subscribe to splitView's notification of subviews resizing
    // (I do this in -(void)awakeFromNib)
    [[NSNotificationCenter defaultCenter]
     addObserver:self
     selector:@selector(mainSplitViewWillResizeSubviewsHandler:)
     name:NSSplitViewWillResizeSubviewsNotification
     object:splitView
     ];
    
    // this is the handler the above snippet refers to
    - (void) mainSplitViewWillResizeSubviewsHandler:(id)object
    {
        lastSplitViewSubViewLeftWidth = [splitViewSubViewLeft frame].size.width;
    }
    
    // wire this to the UI control you wish to use to toggle the
    // expanded/collapsed state of splitViewSubViewLeft
    - (IBAction) toggleLeftSubView:(id)sender
    {
        [splitView adjustSubviews];
        if ([splitView isSubviewCollapsed:splitViewSubViewLeft])
            [splitView
             setPosition:lastSplitViewSubViewLeftWidth
             ofDividerAtIndex:0
             ];
        else
            [splitView
             setPosition:[splitView minPossiblePositionOfDividerAtIndex:0]
             ofDividerAtIndex:0
             ];
    }
    

    【讨论】:

    • 这实际上并没有“折叠”子视图。它只是将其缩小到 0。这是一个问题,因为 -isSubviewCollapsed: 仍然总是返回 NO 并且,如果您的子视图的界面应用了任何自动调整大小的掩码,它们就会变得一团糟。请参阅下面涉及使用-setHidden: 以获得正确答案的解决方案。
    • setPosition:ofDividerAtIndex: 方法不会将子视图缩小到零大小。在上面的代码中,作者将新位置设置为固定值(lastSplitViewSubViewLeftWidth),但可以使用setPosition:NSMaxX(subview.frame) ofDividerAtIndex:0 恢复折叠前的值。请注意,由于子视图保留其原始宽度,因此不需要通知来存储它。另见:cocoadev.com/wiki/NSSplitView
    • 当我尝试使用索引 1 折叠右侧拆分视图窗格时,此解决方案不起作用。我确信有一些东西可以使它起作用,但并不明显,Andreas Järliden 解决方案有效完美。
    【解决方案3】:

    我尝试了上面的解决方案,发现它不起作用,因为 isSubviewCollapsed 从来没有返回 YES

    建议的组合产生了有效的结果

    if ([splitViewTop isHidden]) {
        [splitViewTop setHidden:NO];
        [split
         setPosition:previousSplitViewHeight
         ofDividerAtIndex:0];
    }
    else {
        [splitViewTop setHidden:YES];
    }
    [split adjustSubviews];
    

    【讨论】:

    • 将 isSubviewCollapsed 更改为 isHidden 是 IMO 设计失败。如果他不熟悉您的问题,将来会遇到一些阅读困难。如果绝对建议 hasseg 的解决方案。
    • 这里的混乱似乎集中在“崩溃”的定义上。折叠视图是否意味着将其大小设置为 0,或隐藏它。 NSSplitViewDelegate 协议在 splitView:canCollapseSubview 中给出了这个定义:“折叠的子视图是隐藏的,但由拆分视图对象保留,其大小与折叠前的大小相同。”所以,@jemmons 和 Milliways 似乎在它的右边。
    【解决方案4】:

    在 El Capitan 中,这对我有用。

    splitViewItem.collapsed = YES;
    

    【讨论】:

    • 什么是splitViewItem?这似乎很有希望,但它没有说明实际做什么或如何实际做
    • NSSplitViewItem 是 NSSplitViewController 使用的一个类,它简化了折叠但设置起来比较复杂。
    【解决方案5】:

    在尝试了一些建议后,这是我发现的最简单的解决方案:

    -(void)toggleCollapsibleView:(ib)sender {
       [collapsibleView setHidden:![splitView isSubviewCollapsed:collapsibleView]];
       [splitView adjustSubviews];
    }
    

    该功能是用户定义的第一响应者动作。它由菜单项(或击键)触发。 collapsibleView 是 splitView 中的一个子视图,两者在 IB 中通过它们的属性连接。

    【讨论】:

      【解决方案6】:

      在 macOS Sierra 中,collapsed 属性更改为 isCollapsed。直接将属性设置为truefalse。以下代码来自我的WindowController,其中我有两个SplitViewItems。

      @IBAction func toggleMap(_ sender: Any) {
          if let splitViewController = contentViewController as? NSSplitViewController {
              let splitViewItem = splitViewController.splitViewItems
              if splitViewItem.first!.isCollapsed {
                  splitViewItem.first!.isCollapsed = false
              } else if splitViewItem.last!.isCollapsed {
                  splitViewItem.last!.isCollapsed = false
              } else {
                  if splitViewItem.first!.isCollapsed {
                      splitViewItem.first!.isCollapsed = false
                  }
                  splitViewItem.last!.isCollapsed = true
              }
          }
      }
      

      【讨论】:

      • 这也适用于我。不过,看起来并没有一种方法可以对其打开和关闭进行动画处理。我想知道 Xcode 是如何做到的。它的侧边栏滑动打开和关闭。 ?
      • 您可以在动画期间更改侧边栏的宽度以获得相同的结果。
      • 我已经了解了动画代理,所以你可以像这样添加动画:splitViewItem.animator().isCollapsed = false。效果很好。
      • 感谢克利夫顿,现在将其添加到我自己的应用程序中 :-)
      【解决方案7】:

      NSSplitView 实际上有一个 私有方法 -(void)_setSubview:(NSView *)view isCollapsed:(BOOL)collapsed 可以做到这一点。那些想忽略所有反对使用私有方法的警告的人,看:

      - (void)toggleSubview:(NSView *)view {
          SEL selector = @selector(_setSubview:isCollapsed:);
          NSMethodSignature *signature = [NSSplitView instanceMethodSignatureForSelector:selector];
          NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
          invocation.target = self;
          invocation.selector = selector;
          [invocation setArgument:&view atIndex:2];
          BOOL arg = ![self isSubviewCollapsed:view];
          [invocation setArgument:&arg atIndex:3];
          [invocation invoke];
      }
      

      我在NSSplitView 上将其作为一个类别实现。唯一的问题是 Xcode 给出了关于 _setSubview:isCollapsed: 未声明的警告......我不太确定如何解决这个问题。


      El Capitan 更新

      我已经有大约 2 年没有为 OS X 编写任何代码了,所以我无法验证这一点,但根据下面 cmets 中的 lemonmojo_setSubview:isCollapsed: 在 El Capitan 中被重命名为 @ 987654328@。

      【讨论】:

      • 我会更新,让您知道这是否可以在 Mac App Store 上运行。我知道 Apple 有禁止使用私有方法的规则,但由于 Obj-C 是一种动态语言,我怀疑必须有一种方法(可能我自己在这个答案中的代码完成它)来躲避他们的检查器。
      • 虽然这在技术上是一种私有方法,但我怀疑它可能是每个 OS X 版本(过去、现在和未来)的最佳解决方案。当然,直到 Apple 推出一种公开的方法来完成同样的事情。
      • 我以前使用过这种私有方法,但它似乎已在 El Capitan 中被删除。 respondsToSelector:现在返回 NO 并且无论如何调用该方法都会导致错误。
      • @lemonmojo - 太糟糕了。您可以尝试打印出该类的所有方法...我有一段时间没有使用 Obj-C,所以我不记得您是如何做到的...但是您可以这样做,甚至可以在操场上在 Xcode 中,确定 Apple 是否添加了一些允许您以编程方式展开/折叠子视图的新方法。
      • 好点!我实际上通过手动调整子视图的框架来解决这个问题。但是,如果有人正在寻找类似于使用“_setSubview:isCollapsed:”的解决方案,“_setArrangedView:isCollapsed:”似乎对 El Capitan 有用。
      【解决方案8】:

      这很快就奏效了

      func togglePanel() {
          let splitViewItem = self.mySplitView.arrangedSubviews
      
          if mySplitView.isSubviewCollapsed(outline.view){
              splitViewItem[0].hidden = false
          } else {
              splitViewItem[0].hidden = true
          }
      

      从 IBAction 调用它, outline 是一个带有自己的 xib 的 OutlineViewController,因此我们需要视图,因此需要使用 outline.view,保持简单,但希望您能理解

      @IBAction func segmentAction(sender: NSSegmentedControl) {
          splitVC?.togglePanel(sender.selectedSegment)
      }
      

      func togglePanel(segmentID: Int) {
          let splitViewItem = self.mySplitView.arrangedSubviews
      
          switch segmentID {
      
          case segmentID:
              if mySplitView.isSubviewCollapsed(splitViewItem[segmentID]) {
                  splitViewItem[segmentID].hidden = false
              } else {
                  splitViewItem[segmentID].hidden = true
              }
          default:
              break
          }
      
      }
      

      并实现委托

      func splitView(splitView: NSSplitView, shouldHideDividerAtIndex dividerIndex: Int) -> Bool {
          return true
      }
      

      在 10.11 中,您可能只需要使用 toggleSidebar 操作方法。 How to toggle visibility of NSSplitView subView + hide Pane Splitter divider? https://github.com/Dis3buted/SplitViewController

      【讨论】:

      • toggleSidebar(_:) 属于NSSplitViewController,而不是NSSplitView。因此,如果您愿意(并且能够)使用它,它只是解决问题的方法。
      • 当然是这样。但是,您可以单独使用NSSplitView(就像我现在正在做的那样),然后您就没有可用的NSSplitViewController API。 OP 的问题与控制器无关,都是关于 NSSplitView
      • 这是一个很好的接触,也是一个很棒的 AP​​I 使用。只是不清楚在哪里寻找它,因为它不在视图本身上,而是在整个其他类上。从您链接到的原始帖子中也不清楚。这两个问题都没有提到控制器,标签也只说NSSplitView。因此,除非您在来这里时已经设置了 NSSplitViewController,否则您的插件不是解决方案,也不是找到解决方案的简单方法。
      【解决方案9】:

      我建议改用NSSplitViewController,并使用NSSplitViewItem.isCollapsed 来控制它们。这行得通。

      let item: NSSplitViewItem = ...
      item.isCollapsed = true
      

      要使其正常工作,您必须配置主要使用视图控制器的拆分 UI 组件。否则,它可能会被破坏。

      【讨论】:

        【解决方案10】:

        你可以试试 Brandon Walkin 的BWToolKit

        BWSplitView 类有一个方法

        - (IBAction)toggleCollapse:(id)sender;
        

        【讨论】:

        • BWToolKit 有非常好的控件。没有反对插件。但我认为NSSplitView 现在能够做到RBSplitView 可以做到的所有技巧?
        猜你喜欢
        • 2011-07-06
        • 1970-01-01
        • 2012-07-01
        • 2018-06-17
        • 2019-11-11
        • 2015-08-19
        • 2017-05-07
        • 1970-01-01
        • 2014-12-18
        相关资源
        最近更新 更多