NSTableView: как рисовать пользовательские разделители до и после выбранной строки

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

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

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

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

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

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

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

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

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

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


person Francisco Adasme    schedule 09.12.2011    source источник


Ответы (1)


Я также попробовал это и заметил, что 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;
}

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

person Tim    schedule 02.01.2012
comment
На самом деле документация Apple оказалась правильной: поведение NSTableRowView по умолчанию заключается в том, чтобы рисовать разделительную линию внизу строки, а не вверху. Я соответствующим образом отредактировал приведенный выше пример кода. - person Tim; 03.01.2012
comment
Большое спасибо, это определенно делает то, что я пытался сделать. Однако всякий раз, когда я выбираю строку или прокручиваю таблицу, предыдущая выбранная строка перерисовывается несколько раз, что создает сверхжирный текст и другие странные эффекты. Я что-то пропустил? - person Francisco Adasme; 22.01.2012
comment
Я отлично работал с моим старым кодом. Однако я переместил фрагмент кода, который заставляет предыдущее представление ячейки отрисовываться, расположенный в конце метода drawSeparatorInRect:, в метод делегата, который дает мне новую выбранную строку (я использовал tableView:shouldSelectRow:). Еще раз спасибо за вашу помощь. - person Francisco Adasme; 27.01.2012
comment
Я бы предпочел поместить вызов setNeedsDisplayInRect: в метод делегата outlineViewSelectionDidChange:. - person Vincent Tourraine; 25.02.2014