NSSplitVIew - автоматическое сохранение позиций разделителя не работает при включенной автоматической компоновке

При автоматическом раскладе, автоматическое сохранение позиций разделителя путем установки имени автосохранения для NSSplitView в построителе интерфейса приводит к тому, что каждый разделитель полностью сбрасывается при перезапуске приложения. Отключение автоматического макета позволяет автоматически сохранять изображения.

Я тоже пробовал это в новом проекте Xcode, тот же результат. Это ошибка или известная несовместимость?

Как я мог обойти это (или есть ли исправление к этому, если это ошибка)?

Ответ 1

Я обнаружил, что установка Identifier и Autosave внутри раскадровки с включенным автозапуском не работает. Однако это сработало для меня, как только я программно установил autosaveName.

class MySplitViewController: NSSplitViewController {

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

Ответ 2

Я столкнулся с этой проблемой, и я обнаружил, что мне нужно установить значения identifier и autosaveName для NSSplitView и что они должны быть установлены на разные значения.

Ответ 3

Для меня установка идентификатора + 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 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 ) ];
            }
        }
    }
}

Ответ 4

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 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];
}

Ответ 5

Я обнаружил, что использование NSSplitView ужасно в режиме автоматического макета. Поэтому я написал разделение на основе автозапуска: https://github.com/silvansky/TwinPanelView

Он может хранить позицию своего рукояти (не полностью автоматизирован).

Ответ 6

Нашел себя, глядя на те же старые проблемы с автосохранением NSSplitView с лет назад с Mac OS 10.12. К счастью, решение Joris по-прежнему является отличным решением. Здесь это проверенное расширение Swift 3, которое прекрасно работает в нашем текущем проекте.

Примечание. Так как автоматический макет, по-видимому, переопределяет значения автосохранения по умолчанию после awakeFromNib в NSSplitView. restoreAutoSavePositions() необходимо вызвать в viewDidLoad или около этого контроллера представления, чтобы заставить это работать.

extension NSSplitView {

    /*
    ** unfortunately this needs to be called in the controller viewDidAppear function as
    ** auto layout kicks in to override any default values after the split view 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)
            }
        }
    }
}

Ответ 7

В зависимости от вашего случая представление может не находиться в иерархии представлений при первом его создании. В этом случае autosaveName будет работать только в том случае, если оно установлено ПОСЛЕ того, как представление было добавлено в иерархию представления Windows.