【问题标题】:NSSplitVIew - auto saving divider positions doesn't work with auto layout enabledNSSplitVIew - 自动保存分隔位置在启用自动布局的情况下不起作用
【发布时间】:2013-05-16 12:10:04
【问题描述】:

启用自动布局后,通过在界面构建器中为 NSSplitView 设置自动保存名称来自动保存分隔线位置会导致每个分隔线在应用重新启动时完全折叠。禁用自动布局允许自动保存完美地工作。

我也在一个新的 Xcode 项目中尝试过这个,同样的结果。这是一个错误,还是已知的不兼容?

我该如何解决这个问题(或者如果它是一个错误,是否可以解决这个问题)?

【问题讨论】:

  • 它在我的新项目中运行良好。您是否为拆分视图指定了自动保存名称?测试时如何停止程序?使用 Cmd-Q,还是 XCode 中的停止按钮?

标签: objective-c macos cocoa autolayout nssplitview


【解决方案1】:

我发现在启用自动布局的 Storyboard 中设置 IdentifierAutosave 不起作用。但是,一旦我以编程方式设置 autosaveName,它确实对我有用。

class MySplitViewController: NSSplitViewController {

    override func viewDidLoad() {
        super.viewDidLoad()            
        splitView.autosaveName = "Please Save Me!"
    }
}

        

【讨论】:

  • 这个解决方案对我来说非常完美,而且比上述解决方法简单得多。
  • 这也是我的!我想为情节提要上的不同拆分视图重用 MySplitViewController,并发现我为情节提要中的每个拆分视图控制器输入的值在 MySplitViewController 中名为“标识符”的成员变量中的“身份检查器”->“恢复 ID”表面。您可以通过说:“splitView.autosaveName = identifier”将其分配给上面的自动保存值
  • 哇,这个答案太好了!
  • 对于 Swift 4,它是:splitView.autosaveName = NSSplitView.AutosaveName(rawValue: "Please Save Me! Really!")
  • 在 Swift 5 中它又回到了 splitView.autosaveName = "Please Save Me!"
【解决方案2】:

我也遇到了这个问题,我发现我需要为 NSSplitView 设置 both identifierautosaveName 值,并且需要将它们设置为不同的价值观。

【讨论】:

  • 我已经为此苦苦挣扎了一段时间,终于找到了你的答案。不敢相信这是像标识符一样愚蠢的东西。但是将其设置为与我的自动保存名称不同的名称是一种享受。谢谢!
  • identifier 是什么意思?该属性在哪里定义?
  • @KonstantinPavlikhin NSUserInterfaceItemIdentification,大多数(全部?)视图都实现了这一点。
  • 顺便说一句,没有区别,分屏状态不保存。
【解决方案3】:

根据您的情况,当您第一次实例化视图时,视图可能不在视图层次结构中。如果是这种情况,autosaveName 只有在视图被添加到 Windows 视图层次结构之后设置才有效,因此您可以考虑在中设置自动保存名称

func viewDidMoveToWindow() {
    super.viewDidMoveToWindow()
    splitView.autosaveName = "mySplitViewState"
}

【讨论】:

  • 根据我的测试,这应该是公认的答案。也许不完美,但如果你把它放在“func viewWillLayout()”函数中,它就可以了。
【解决方案4】:

对我来说,设置标识符 + 自动保存名称不起作用。我不得不求助于 ElmerCat 提供的解决方案。但是我稍微修改了代码以避免设置分隔线位置(没有让它工作)。相反,我正在修改视图大小。我还添加了折叠视图的隐藏功能。

@interface NSSplitView (RestoreAutoSave)
- (void)restoreAutosavedPositions;
@end

@implementation NSSplitView (RestoreAutoSave)
- (void)restoreAutosavedPositions
{
    NSString *key = [NSString stringWithFormat:@"NSSplitView Subview Frames %@", self.autosaveName];
    NSArray *subviewFrames = [[NSUserDefaults standardUserDefaults] valueForKey:key];

    // the last frame is skipped because I have one less divider than I have frames
    for( NSInteger i = 0; i < subviewFrames.count; i++ ) {

        if( i < self.subviews.count ) { // safety-check (in case number of views have been removed while dev)

            // this is the saved frame data - it's an NSString
            NSString *frameString = subviewFrames[i];
            NSArray *components = [frameString componentsSeparatedByString:@", "];

            // Manage the 'hidden state' per view
            BOOL hidden = [components[4] boolValue];
            NSView* subView =[self subviews][i];
            [subView setHidden: hidden];

            // Set height (horizontal) or width (vertical)
            if( !self.vertical ) {

                CGFloat height = [components[3] floatValue];
                [subView setFrameSize: NSMakeSize( subView.frame.size.width, height ) ];
            }
            else {

                CGFloat width = [components[2] floatValue];
                [subView setFrameSize: NSMakeSize( width, subView.frame.size.height ) ];
            }
        }
    }
}

【讨论】:

  • NSSplitView 中的错误仍然使这变得必要,并且此修复仍然有效。谢谢。这应该是公认的答案。要求设置 identifier 的问题似乎已由 Apple 解决。
【解决方案5】:

NSSplitView 因特别挑剔和麻烦而臭名昭著;有时您必须竭尽全力使其正常运行。我知道我的设置保存在User Defaults - 我可以通过终端“Defaults read etc...”看到它们正确更改,但是当应用程序重新打开时它们没有恢复。

我通过在awakeFromNib 期间手动读取保存的值并恢复分隔符位置解决了这个问题。

这是 NSSplitView 上的一个类别,它礼貌地要求它将其分隔线位置设置为其自动保存的值:

@interface NSSplitView (PraxCategories)
- (void)restoreAutosavedPositions;
@end

@implementation NSSplitView (PraxCategories)
- (void)restoreAutosavedPositions {

    // Yes, I know my Autosave Name; but I won't necessarily restore myself automatically.
    NSString *key = [NSString stringWithFormat:@"NSSplitView Subview Frames %@", self.autosaveName];

    NSArray *subviewFrames = [[NSUserDefaults standardUserDefaults] valueForKey:key];

    // the last frame is skipped because I have one less divider than I have frames
    for (NSInteger i=0; i < (subviewFrames.count - 1); i++ ) {

        // this is the saved frame data - it's an NSString
        NSString *frameString = subviewFrames[i];
        NSArray *components = [frameString componentsSeparatedByString:@", "];

        // only one component from the string is needed to set the position
        CGFloat position;

        // if I'm vertical the third component is the frame width
        if (self.vertical) position = [components[2] floatValue];

        // if I'm horizontal the fourth component is the frame height
        else position = [components[3] floatValue];

        [self setPosition:position ofDividerAtIndex:i];
    }
}
@end

然后只需在awakeFromNib 期间为您希望恢复的每个NSSplitView 调用该方法:

for (NSSplitView *splitView in @[thisSplitView, thatSplitView, anotherSplitView]) {
    [splitView restoreAutosavedPositions];
}

【讨论】:

    【解决方案6】:

    我发现在自动布局模式下使用 NSSplitView 很糟糕。所以我写了基于自动布局的拆分视图:https://github.com/silvansky/TwinPanelView

    它可以存储它的手柄位置(不是完全自动化的)。

    【讨论】:

      【解决方案7】:

      发现自己在使用 Mac OS 10.12 时正在查看多年前相同的旧 NSSplitView 自动保存问题。令人高兴的是,Joris 的解决方案仍然是一个很好的解决方法。这是一个经过测试的 Swift 3 扩展,在我们当前的项目中运行良好。

      注意:由于自动布局显然会在 NSSplitView 中的 awakeFromNib 之后覆盖自动保存默认值,因此需要在视图控制器的 viewDidLoad 或附近调用 restoreAutoSavePositions() 才能使其正常工作。

      extension NSSplitView {
      
          /*
          ** unfortunately this needs to be called in the controller's viewDidAppear function as
          ** auto layout kicks in to override any default values after the split view's awakeFromNib
          */
          func restoreAutoSavePositions() {
      
              let key = String(format: "NSSplitView Subview Frames %@", self.autosaveName!)
              let subViewFrames = UserDefaults.standard.array(forKey: key)
              guard subViewFrames != nil else { return }
      
              for (i, frame) in (subViewFrames?.enumerated())! {
      
                  if let frameString = frame as? String {
      
                      let components = frameString.components(separatedBy: ", ")
                      guard components.count >= 4 else { return }
      
                      var position: CGFloat = 0.0
      
                      // Manage the 'hidden state' per view
                      let hidden = NSString(string:components[4].lowercased()).boolValue
                      let subView = self.subviews[i]
                      subView.isHidden = hidden
      
                      // Set height (horizontal) or width (vertical)
                      if self.isVertical {
                          if let n = NumberFormatter().number(from: components[2]) {
                              position = CGFloat(n)
                          }
                      } else {
                          if let n = NumberFormatter().number(from: components[3]) {
                              position = CGFloat(n)
                          }
                      }
      
                      setPosition(position, ofDividerAt: i)
                  }
              }
          }
      }
      

      【讨论】:

        【解决方案8】:

        我正在使用 NSSplitViewController,我只看到未恢复的项目的折叠状态(尺寸正确)。我可以看到信息被正确保存了。

        根据此处的各种其他答案,我创建了以下扩展:

        extension NSSplitViewController
        {
            public func ensureRestoreCollapsed()
            {
                guard let autosaveName = splitView.autosaveName else { return }
        
                let framesKey = "NSSplitView Subview Frames \(autosaveName)"
                guard let subViewFrames = UserDefaults.standard.array(forKey: framesKey) else { return }
        
                for (i, frame) in subViewFrames.enumerated() {
                    guard let hidden = (frame as? String)?.components(separatedBy: ", ")[safe: 4] else {
                        return }
                    
                    splitViewItems[safe: i]?.isCollapsed = hidden.boolValue
                }
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多