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.