NSSplitVIew - 自动保存分隔符位置不适用于启用自动布局

时间:2013-05-16 12:10:05

标签: objective-c macos cocoa autolayout nssplitview

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

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

我如何解决这个问题(如果是错误的话,是否有解决方法)?

8 个答案:

答案 0 :(得分:21)

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

class MySplitViewController: NSSplitViewController {

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

答案 1 :(得分:9)

我也遇到了这个问题,我发现我需要为NSSplitView设置两个 identifierautosaveName值,并且他们需要被设置为不同的值。

答案 2 :(得分:4)

对我来说,设置标识符+ autosavename不起作用。我不得不依靠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 ) ];
            }
        }
    }
}

答案 3 :(得分: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];
}

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

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

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

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

答案 6 :(得分:0)

根据您的情况,首次实例化视图时,该视图可能不在视图层次结构中。在这种情况下,autosaveName仅在将视图添加到Windows视图层次结构之后才设置为有效。

答案 7 :(得分:-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
        }
    }
}
相关问题