Подтвердить что ты не робот

NSSplitView: управление позицией разделителя при изменении размера окна

У меня есть NSSplitView, у которого есть две панели - вид боковой панели слева и веб-представление справа. У меня также есть набор делегатов, который обрабатывает ограничения для боковой панели следующим образом:

- (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex {
    return 500.0f;
}

- (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
    return 175.0f;
}

- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview {
    return NO;
}

Это означает, что боковая панель может быть изменена только между 175 и 500 пикселями, и это отлично работает при использовании разделителя. Но при изменении размера всего окна делитель вытесняется из этих ограничений.

Кто-нибудь знает, как это контролировать?

Дополнительно: если я хочу сохранить пользовательский выбор ширины боковой панели, хорошо подумать, чтобы прочитать его, сохранить в файл настроек и восстановить его позже, или есть более прямой способ сделать это? Я заметил, что состояние окна сохраняется в некоторых случаях - обычно ли это происходит или мне нужно его контролировать?

Заранее спасибо

Арне

4b9b3361

Ответ 1

Что вы ищете:

- (void)splitView:(NSSplitView*)sender resizeSubviewsWithOldSize:(NSSize)oldSize

[sender frame] будет новый размер вашего NSSplitView после изменения размера. Затем просто отрегулируйте свои подвид. Соответственно.

Ответ 2

Проблема заключается в том, что при изменении размера NSSplitView -adjustSubviews получает вызов для выполнения работы, но он просто игнорирует ограничения min/max из делегата!

Однако -setPosition:ofDividerAtIndex: учитывает ограничения.

Все, что вам нужно сделать, это объединить оба - этот пример предполагает NSSplitView с двумя видами:

- (CGFloat)splitView:(NSSplitView*)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
  return 300;
}

- (CGFloat)splitView:(NSSplitView*)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex {
  return (splitView.vertical ? splitView.bounds.size.width : splitView.bounds.size.height) - 500;
}

- (void)splitView:(NSSplitView*)splitView resizeSubviewsWithOldSize:(NSSize)oldSize {
  [splitView adjustSubviews];  // Use default resizing behavior from NSSplitView
  NSView* view = splitView.subviews.firstObject;
  [splitView setPosition:(splitView.vertical ? view.frame.size.width : view.frame.size.height) ofDividerAtIndex:0];  // Force-apply constraints afterwards
}

Это, как представляется, отлично работает на OS X 10.8, 10.9 и 10.10 и намного чище, чем другие подходы, поскольку это минимальный код, а ограничения не дублируются.

Ответ 3

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

Затем я изменил свой подход и нашел чистое и чрезвычайно простое решение. Я просто установил константу автоматического макета для ширины ( >= для моего желаемого минимального размера) на NSView для одной стороны NSSplitView. Я сделал то же самое с другой стороны. С этими двумя простыми ограничениями NSSplitView работал отлично, без необходимости делегирования вызовов.

Ответ 4

Альтернативный способ решения этой проблемы - использовать splitView:shouldAdjustSizeOfSubview:

Я нашел это намного проще для моих целей.

Например, если вы хотите, чтобы sidebarTableView никогда не был меньше минимальной ширины 175, вы можете сделать что-то вроде этого (при условии, что вы создали sidebarTableView на вашем контроллере/делегате),

- (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)subview
{
    if ((subview==self.sidebarTableView) && subview.bounds.size.width<=175) {
        return NO;
    }
    return YES;
}

Ответ 5

Здесь моя реализация -splitView:resizeSubviewsWithOldSize::

-(void)splitView:(NSSplitView *)splitView resizeSubviewsWithOldSize:(NSSize)oldSize {
    if (![splitView isSubviewCollapsed:self.rightView] &&
        self.rightView.frame.size.width < 275.0f + DBL_EPSILON) {
        NSSize splitViewFrameSize = splitView.frame.size;
        CGFloat leftViewWidth = splitViewFrameSize.width - 275.0f - splitView.dividerThickness;
        self.leftView.frameSize = NSMakeSize(leftViewWidth,
                                             splitViewFrameSize.height);
        self.rightView.frame = NSMakeRect(leftViewWidth + splitView.dividerThickness,
                                          0.0f,
                                          275.0,
                                          splitViewFrameSize.height);
    } else
        [splitView adjustSubviews];
}

В моем случае rightView является вторым из двух подзонов, который сжимается с минимальной шириной 275,0. leftView не имеет минимума или максимума и не сжимается.

Ответ 6

Я использовал

- (void)splitView:(NSSplitView*)sender resizeSubviewsWithOldSize:(NSSize)oldSize

но вместо изменения рамки subview я использовал

    [sender setPosition:360 ofDividerAtIndex:0]; //or whatever your index and position should be

Изменение рамки не дало мне последовательных результатов. Настройка положения разделителя.

Ответ 7

Возможно, слишком поздно для вечеринки, однако это моя реализация resizeSubviewWithOldSize:. В моем проекте мне нужен вертикальный масштабируемый NSplitView с шириной leftView между 100.0 и 300.0; нет "сворачивания", принятого во внимание. Вы должны позаботиться обо всех возможных измерениях для подсмотров.

-(void)splitView:(NSSplitView *)splitView resizeSubviewsWithOldSize:(NSSize)oldSize {
    if (self.leftView.frame.size.width >= kMaxLeftWidth) {
        NSSize splitViewFrameSize = splitView.frame.size;
        CGFloat leftViewWidth = kMaxLeftWidth;
        CGFloat rightViewWidth = splitViewFrameSize.width - leftViewWidth - splitView.dividerThickness;

        self.leftView.frameSize = NSMakeSize(leftViewWidth,
                                             splitViewFrameSize.height);

        self.rightView.frame = NSMakeRect(leftViewWidth + splitView.dividerThickness,
                                          0.0f,
                                          rightViewWidth,
                                          splitViewFrameSize.height);
    } else if (self.leftView.frame.size.width <= kMinLeftWidth) {
        NSSize splitViewFrameSize = splitView.frame.size;
        CGFloat leftViewWidth = kMinLeftWidth;
        CGFloat rightViewWidth = splitViewFrameSize.width - leftViewWidth - splitView.dividerThickness;

        self.leftView.frameSize = NSMakeSize(leftViewWidth,
                                             splitViewFrameSize.height);

        self.rightView.frame = NSMakeRect(leftViewWidth + splitView.dividerThickness,
                                          0.0f,
                                          rightViewWidth,
                                          splitViewFrameSize.height);
    } else {
        NSSize splitViewFrameSize = splitView.frame.size;
        CGFloat leftViewWidth = self.leftView.frame.size.width;
        CGFloat rightViewWidth = splitViewFrameSize.width - leftViewWidth - splitView.dividerThickness;

        self.leftView.frameSize = NSMakeSize(leftViewWidth,
                                             splitViewFrameSize.height);

        self.rightView.frame = NSMakeRect(leftViewWidth + splitView.dividerThickness,
                                          0.0f,
                                          rightViewWidth,
                                          splitViewFrameSize.height);
    }
}

Ответ 8

Я достиг такого поведения, установив holdingPriority на NSSplitViewItem в Required (1000) для фиксированной стороны в Interface Builder. Затем вы можете контролировать ширину для фиксированной стороны, установив ограничение на базовый NSView.

Ответ 9

Мне просто нужно было это сделать, и придумал этот метод, который немного проще предыдущих примеров. Этот код предполагает, что существуют IBOutlets для левого и правого NSScrollViews контейнера NSSplitView, а также константы CGFloat для минимального размера левого и правого представлений.

#pragma mark - NSSplitView sizing override

//------------------------------------------------------------------------------
// This is implemented to ensure that when the window is resized that our main table
// remains at it smallest size or larger (the default proportional sizing done by
// -adjustSubviews would size it smaller w/o -constrainMinCoordiante being called).

- (void) splitView: (NSSplitView *)inSplitView resizeSubviewsWithOldSize: (NSSize)oldSize
{
    // First, let the default proportional adjustments take place
    [inSplitView adjustSubviews];

    // Then ensure that our views are at least their min size
    // *** WARNING: this does not handle allowing the window to be made smaller than the total of the two views!

    // Gather current sizes
    NSSize leftViewSize = self.leftSideView.frame.size;
    NSSize rightViewSize = self.rightSideView.frame.size;
    NSSize splitViewSize = inSplitView.frame.size;
    CGFloat dividerWidth = inSplitView.dividerThickness;

    // Assume we don't have to resize anything
    CGFloat newLeftWidth = 0.0f;

    // Always adjust the left view first if we need to change either view size
    if( leftViewSize.width < kLeftSplitViewMinSize )
    {
        newLeftWidth = kLeftSplitViewMinSize;
    }
    else if( rightViewSize.width < kRightSplitViewMinSize )
    {
        newLeftWidth = splitViewSize.width - (kRightSplitViewMinSize + dividerWidth);
    }

    // Do we need to adjust the size?
    if( newLeftWidth > 0.0f )
    {
        // Yes, do so by setting the left view and setting the right view to the space left over
        leftViewSize.width = newLeftWidth;
        rightViewSize.width = splitViewSize.width - (newLeftWidth + dividerWidth);

        // We also need to set the origin of the right view correctly
        NSPoint origin = self.rightSideView.frame.origin;
        origin.x = splitViewSize.width - rightViewSize.width;
        [self.rightSideView setFrameOrigin: origin];

        // Set the the ajusted view sizes
        leftViewSize.height = rightViewSize.height = splitViewSize.height;
        [self.leftSideView setFrameSize: leftViewSize];
        [self.rightSideView setFrameSize: rightViewSize];
    }
}