NSTableView: как нарисовать пользовательские разделители до и после выбранной строки - PullRequest
9 голосов
/ 09 декабря 2011

Это мой первый вопрос здесь, и я постараюсь сделать его максимально понятным.

Я хочу нарисовать пользовательский градиент в выбранной строке в NSTableView на основе вида, добавив при этом тонкий эффект повышения. Для этого мне нужно использовать более темный цвет для линий сетки, которые находятся до и после выбранной строки (см. здесь для примера). Я переопределил метод drawSeparatorInRect: в NSTableRowView , чтобы нарисовать линию специального разделителя для выбранной строки (используя метод isSelected в качестве флага), но я не могу сделать то же самое для выше / ниже (так как Я рисую линию внизу / вверху).

Я пробовал несколько способов сказать ближайшей строке, что она должна рисовать более темную разделительную линию, но безуспешно, поскольку шаг отображения не следует тому же порядку (я проверил это с помощью NSLogs в drawSeparatorInRect:, и кажется, что когда вы прокручиваете немного, этот порядок меняется). Таким образом, иногда (в основном после прокрутки) строка не знает, что она должна использовать более темный цвет, поскольку она рисует сама перед выбранным (я думаю, что в данный момент выбранная строка еще не знает, что она выбрана, в противном случае я не понимаю, что происходит).

Некоторые вещи, которые я пробовал:

  1. В методе drawSeparatorInRect: выбранной строки я попытался получить доступ к представлениям братьев и сестер ([superview subviews]) и заставить предыдущий / следующий снова нарисовать себя.
  2. Из подкласса NSTableView измените непосредственно ближайшую строку при изменении selectedIndexes .
  3. Рисование линии вне выбранной строки из метода drawSeparatorInRect:, как показано здесь .

Обратите внимание, что я сделал это, имея: представление строки, спрашивающее, выбран ли предыдущий / следующий, флаг closestRowIsSelected или внешний вызов метода, чтобы "вызвать" темный цвет.

Теперь у меня есть то, что выбранная строка рисует верхнюю и нижнюю границы, поэтому одна из них помещается вместе на предыдущую / следующую строку строки ... Это тонко, но все еще там.

Любая помощь будет хорошо принята.

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

! Я не опубликовал никакого кода, так как проблема не существует (он просто вызывает [NSBezierPath fillRect:rect] красным цветом), я думаю ... поэтому мне нечего показать.

1 Ответ

12 голосов
/ 03 января 2012

Я также попробовал это и заметил, что drawSeparatorInRect: действительно может нарисовать только свою нижнюю разделительную линию, поскольку позиция верхней разделительной линии (которая совпадает с нижней разделительной линией в предыдущей строке) находится на один пиксель снаружи ( выше) clipRect строки.

Тем не менее, я получил его, работая с подклассами NSTableRowView и имея drawSeparatorInRect: следующим образом:

- (void)drawSeparatorInRect:(NSRect)dirtyRect
{
    // Define our drawing colors
    NSColor *normalColor = [NSColor colorWithCalibratedWhite:0.76 alpha:1.0]; // Default separator color
    NSColor *selectedTopColor = [NSColor colorWithCalibratedWhite:0.60 alpha:1.0]; // Color of the top separator line of selected row
    NSColor *selectedBottomColor = [NSColor colorWithCalibratedWhite:0.60 alpha:1.0]; // Color of the bottom separator line of selected row

    // Define coordinates of separator line
    NSRect drawingRect = [self frame]; // Ignore dirtyRect
    drawingRect.origin.y = drawingRect.size.height - 1.0;
    drawingRect.size.height = 1.0; // Height of the separator line we're going to draw at the bottom of the row

    // Get the table view and info on row index numbers
    NSTableView *tableView = (NSTableView*)[self superview]; // The table view the row is part of
    NSInteger selectedRowNumber = [tableView selectedRow];
    NSInteger ownRowNumber = [tableView rowForView:self];

    // Set the color of the separator line
    [normalColor set]; // Default
    if (([self isSelected]) && ((selectedRowNumber + 1) < [tableView numberOfRows])) [selectedBottomColor set]; // If the row is selected, use selectedBottomColor
    if ((![self isSelected]) && (selectedRowNumber > 0) && (ownRowNumber == (selectedRowNumber-1))) [selectedTopColor set]; // If the row is followed by the selected row, draw its bottom separator line in selectedTopColor

    // Draw separator line
    NSRectFill (drawingRect);

    // If the row is selected, tell the preceding row to redraw its bottom separator line (which is also the top line of the selected row)
    if (([self isSelected]) && (selectedRowNumber > 0)) [tableView setNeedsDisplayInRect:[tableView rectOfRow:selectedRowNumber-1]];
}

Этот метод (только) нарисует свою собственную нижнюю разделительную линию. Если это выбранная строка, она нарисует линию не с цветом по умолчанию, а будет выделена, затем она также скажет предыдущей строке перерисовать свою разделительную линию, то есть так же, как верхняя разделительная линия выбранной строки.

Чтобы это работало, строка над выбранной строкой должна перерисовать нижнюю разделительную линию после перемещения выделения. Я достиг этого, имея этот метод в делегате NSTableView:

// Tell the row above the row which is going to loose the selection to redraw its bottom separator line
- (BOOL)selectionShouldChangeInTableView:(NSTableView *)aTableView
{
    NSInteger selectedRowNumber = [aTableView selectedRow];
    if (selectedRowNumber > 0) {
    [aTableView setNeedsDisplayInRect:[aTableView rectOfRow:selectedRowNumber-1]];
    }
    return YES;
}

Этот метод делегата указывает строке над все еще выбранной строкой перерисовать его разделительную линию. Вызывается непосредственно перед изменением выбора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...