17

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

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

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

4

8 回答 8

32

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

class MySplitViewController: NSSplitViewController {

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

        
于 2015-05-07T13:06:10.813 回答
13

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

于 2013-08-31T16:06:23.223 回答
6

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

func viewDidMoveToWindow() {
    super.viewDidMoveToWindow()
    splitView.autosaveName = "mySplitViewState"
}
于 2019-04-08T17:35:53.983 回答
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 ) ];
            }
        }
    }
}
于 2015-02-04T09:51:01.540 回答
2

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];
}
于 2014-12-31T00:08:17.837 回答
1

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

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

于 2013-05-30T13:39:01.673 回答
1

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

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

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)
            }
        }
    }
}
于 2017-02-03T00:50:20.757 回答
-1

我正在使用 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
        }
    }
}
于 2021-02-05T11:56:46.423 回答