Как изменить цвет фона заголовка NSTableView в приложении MAC OS X? - PullRequest
0 голосов
/ 13 сентября 2018

Я перепробовал все найденные предлагаемые решения, но в итоге получил следующее:

enter image description here

Цель состоит в том, чтобы иметь собственный цвет для:

  1. полный фон заголовка (например, зеленый)
  2. текст (например, белый)
  3. контрольный цвет сортировки (например, белый)

В настоящее времяЯ могу только правильно установить внутреннюю текстуру и цвет текста, оставляя границы заголовка и сортировать элементы управления по умолчанию в белом цвете.

Я использую подход пользовательской NCTableHeaderCell.

// <C> changing the bgColor doesn't work this way
[self.tv.headerView setWantsLayer:YES];
self.tv.headerView.layer.backgroundColor = NSColor.redColor.CGColor;

for (NSTableColumn *tc in self.tv.tableColumns) {

    // <C> this only helps to change the header text color
    tc.headerCell = [[NCTableHeaderCell_customizable alloc]initTextCell:@"Hdr"];

    // <C> this changes the bgColor of the area of the headerCell label text (the interior) but it leaves border and sort controls in white color;
    tc.headerCell.drawsBackground = YES;
    tc.headerCell.backgroundColor = NSColor.greenColor;

    // <C> changing the textColor doesn't work this way
    // <SOLUTION> use NCTableHeaderCell_customizable as done above;
    tc.headerCell.textColor = NSColor.redColor;
}

Мой пользовательский класс выглядит следующим образом:

@implementation NCTableHeaderCell_customizable

// <C> this works as expected
- (NSColor *) textColor
{
    return NSColor.whiteColor;
}

// <C> this only sets the interior bgColor leaving the borders in standard color
//
//- (NSColor *) backgroundColor
//{
//    return NSColor.redColor;
//}

- (void) drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView;
{
    // <C> this only sets the interior bgColor leaving the borders in standard color
    //
    //self.backgroundColor = NSColor.orangeColor;

    [super drawWithFrame:cellFrame inView:controlView];

    // <C> this draws the red bg as expected but doesn't show the interior;
    //
    //    [NSColor.redColor set];
    //    NSRectFillUsingOperation(cellFrame, NSCompositingOperationSourceOver);

    // <C> this draws the red bg as expected but
    //     1) doesn't layout the interior well (I could fix this);
    //     2) doesn't show the sort controls (it's over-drawn with the code bellow);
    //
    //    [NSColor.redColor setFill];
    //    NSRectFill(cellFrame);
    //    CGRect titleRect = [self titleRectForBounds:cellFrame];
    //    [self.attributedStringValue drawInRect:titleRect];
}

- (void) drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView;
{
    [super drawInteriorWithFrame:cellFrame inView:controlView];
}

- (void) drawFocusRingMaskWithFrame:(NSRect)cellFrame inView:(NSView *)controlView;
{
    [super drawFocusRingMaskWithFrame:cellFrame inView:controlView];
}

- (void) drawSortIndicatorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView ascending:(BOOL)ascending priority:(NSInteger)priority;
{
    [super drawSortIndicatorWithFrame:cellFrame inView:controlView ascending:ascending priority:priority];
    //NSTableHeaderView *v = (NSTableHeaderView *)controlView;
}

Я довольно близок к решению, но я не знаю, как правильно нарисовать пользовательскую ячейку заголовка для архивациицель.

Ответы [ 3 ]

0 голосов
/ 19 сентября 2018

Создайте NSTableHeaderCell, как показано ниже, и поиграйтесь с размерами и происхождением фрейма ячейки заголовка:

#import <Cocoa/Cocoa.h>

@interface CustomTableHeaderCell : NSTableHeaderCell {
    NSMutableDictionary *attrs;
}

#import "CustomTableHeaderCell.h"

@implementation CustomTableHeaderCell

- (id)initTextCell:(NSString *)text
{
    if (self = [super initTextCell:text]) {

        if (text == nil || [text isEqualToString:@""]) {
            [self setTitle:@"One"];
        }

        /* old_
        self.textColor = [NSColor blackColor];
         */
        // new_
        self.textColor = [NSColor whiteColor];


        attrs = [[NSMutableDictionary dictionaryWithDictionary:
                  [[self attributedStringValue]
                   attributesAtIndex:0
                   effectiveRange:NULL]]
                 mutableCopy];
//        self.font = [NSFont fontWithName:appleeH8GhKr0 size:12];
        return self;
    }
    return nil;
}

- (void)drawWithFrame:(NSRect)cellFrame
          highlighted:(BOOL)isHighlighted
               inView:(NSView *)view
{
    CGRect fillRect, borderRect;
    CGRectDivide(NSRectToCGRect(cellFrame), &borderRect, &fillRect, 1.0, CGRectMaxYEdge);

    // sets the origin and frame for header's title
//    fillRect.size.height = 25;

    if (fillRect.origin.x == 0)
    {
//        fillRect.origin.y += 5;
       // for adding left margin to first column title try to add spaces in text of header title
       // try to play with the numbers of fillRect rect
    }
    else if (fillRect.origin.x == 239)
    {
//        fillRect.size.width  -= 80;
    }

    // setting the background color of tableview's header view
    NSGradient *gradient = [[NSGradient alloc]
                            initWithStartingColor:[NSColor greenColor]
                            endingColor:[NSColor greenColor]];
    [gradient drawInRect:NSRectFromCGRect(fillRect) angle:90.0];
    [self drawInteriorWithFrame:NSRectFromCGRect(CGRectInset(fillRect, 0.0, 1.0)) inView:view];
 }


- (NSRect)adjustedFrameToVerticallyCenterText:(NSRect)frame
{
    // super would normally draw text at the top of the cell
    NSInteger offsetY = floor((NSHeight(frame) -
                              ([[self font] ascender] - [[self font] descender])) / 2);
    return NSInsetRect(frame, 20, offsetY);
}

-(void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView{
    [super drawInteriorWithFrame:[self adjustedFrameToVerticallyCenterText:cellFrame]
                          inView:controlView];
}

- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)view
{
    [self drawWithFrame:cellFrame highlighted:NO inView:view];
}

- (void)highlight:(BOOL)isHighlighted
        withFrame:(NSRect)cellFrame
           inView:(NSView *)view
{
    [self drawWithFrame:cellFrame highlighted:isHighlighted inView:view];
}

Установите ячейку заголовка для столбца, как показано ниже:

NSArray *columns = nil;
    if(self.tblVc)
        columns = [self.tblVc tableColumns];
    NSEnumerator *cols = [columns objectEnumerator];
    NSTableColumn *col = nil;
    CustomTableHeaderCell *headerCell;

    while (col = [cols nextObject]) {
        NSString *key = [[col headerCell] stringValue];
        headerCell = [[CustomTableHeaderCell alloc]
                      initTextCell:key];

        NSRect rectHeader =self.tblVc.headerView.frame;
        rectHeader.size.height = 25;
        self.tblVc.headerView.frame=rectHeader;

        [col setHeaderCell:headerCell];
    }

и используйте кнопку раскрытияв верхней части заголовка табличного представления, как показано ниже:

storyboard arrangement of sort button

Результаты будут отображаться следующим образом:

custom header with coloured background

0 голосов
/ 20 сентября 2018

Спасибо, ребята, за вашу помощь. Я закончил с этим простым решением, которое также решает показатели сортировки. Я отказался от пользовательского NSHeaderCell и решил все в одном пользовательском NSTableHeaderRowView методе:

- (void)drawRect:(NSRect)dirtyRect
{
    [NSGraphicsContext saveGraphicsState];

    // Row: Fill the background
    //
    [NSColor.whiteColor setFill];
    const CGRect headerRect = CGRectMake(0.0, 1.0, self.bounds.size.width, self.bounds.size.height-1.0);
    [[NSBezierPath bezierPathWithRect:headerRect] fill];

    // Columns
    //
    for (NSUInteger i = 0; i < self.tableView.numberOfColumns; ++i) {
        NSRect rect = [self headerRectOfColumn:i];

        // separator on left
        //
        if (i != 0) {
            [[NSColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.35] setFill];
            [[NSBezierPath bezierPathWithRect:NSMakeRect(rect.origin.x, rect.origin.y+4.0, 1.0, rect.size.height-8.0)] fill];
        }

        NSTableColumn *tableColumn = self.tableView.tableColumns[i];
        NSSortDescriptor *col_sd = tableColumn.sortDescriptorPrototype;
        NSTableHeaderCell *tableHeaderCell = tableColumn.headerCell;

        // text
        //
        NSString *columnText = tableHeaderCell.stringValue;
        [columnText drawInRect:NSInsetRect(rect, 5.0, 4.0) withAttributes:nil];

        // sort indicator
        //
        for (NSInteger priority = 0; priority < self.tableView.sortDescriptors.count; ++priority) {
            NSSortDescriptor *sortDesciptor = self.tableView.sortDescriptors[priority];
            // <C> there is no way to get column from sortDesciptor so I use this trick comparing sortDesciptor with current column.sortDescriptorPrototype;
            // <C> not sure why sel_isEqual() dosn't work so I compare its string representation;
            if ([NSStringFromSelector(sortDesciptor.selector) isEqualToString:NSStringFromSelector(col_sd.selector)] == YES && [sortDesciptor.key isEqualToString:col_sd.key] == YES) {
                SDEBUG(LEVEL_DEBUG, @"sort-hdr", @"MATCH: sel=%@", NSStringFromSelector(sortDesciptor.selector));
                // <C> default implementation draws indicator ONLY for priority 0; Otherwise the indicator would have to graphically show the prio;
                // <C> it is a support for multi-column sorting;
                // <REF> shall you need it: https://stackoverflow.com/questions/46611972/sorting-a-viewbased-tableview-with-multiple-sort-descriptors-doesnt-work
                [tableHeaderCell drawSortIndicatorWithFrame:rect inView:self ascending:sortDesciptor.ascending priority:priority];
            }
        }
    }

    [NSGraphicsContext restoreGraphicsState];
}
0 голосов
/ 16 сентября 2018

Я не нахожу других способов сделать это, просто должен все нарисовать. Надеюсь, это полезно.

CGRect outCellFrame;

- (void) drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView;
   {
   // <C> this only sets the interior bgColor leaving the borders in standard       color
   outCellFrame = cellFrame;
   [super drawWithFrame:cellFrame inView:controlView];
      // <C> this draws the red bg as expected but doesn't show the interior;
   //

   // <C> this draws the red bg as expected but
   //     1) doesn't layout the interior well (I could fix this);
   //     2) doesn't show the sort controls (it's over-drawn with the code bellow);
   //
   //    [NSColor.redColor setFill];
  //    NSRectFill(cellFrame);
  //    CGRect titleRect = [self titleRectForBounds:cellFrame];
  //    [self.attributedStringValue drawInRect:titleRect];
}

 -(NSRect *) outer:(NSRect)rect fromInner: (NSRect)innerRect {
   NSRect * list =  (NSRect *)  malloc(sizeof(rect) * 4);
   NSRect rem;
  NSDivideRect(rect, &list[0], &rem,  innerRect.origin.x - rect.origin.x,    NSRectEdgeMinX);

    NSDivideRect(rect, &list[1], &rem, - innerRect.origin.x - innerRect.size.width + rect.origin.x + rect.size.width , NSRectEdgeMaxX);
    NSDivideRect(rect, &list[2], &rem, innerRect.origin.y - rect.origin.y, NSRectEdgeMinY);
    NSDivideRect(rect, &list[3], &rem,  -innerRect.origin.y - innerRect.size.height + rect.origin.y + rect.size.height  , NSRectEdgeMaxY);
    return list;
 }

  -(void) updateBackground:(CGRect) cellFrame and:(CGRect) innerCellFrame{
  [self.backgroundColor set];
  NSRect * list = [self outer:cellFrame fromInner:innerCellFrame];
  NSRectFillListUsingOperation(list, 4, NSCompositingOperationSourceOver);
 free(list);}



  - (void) drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView;
   {

      [self updateBackground:outCellFrame and:cellFrame];
     [super drawInteriorWithFrame:cellFrame inView:controlView];


    }






  - (void) drawSortIndicatorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView ascending:(BOOL)ascending priority:(NSInteger)priority;
  {
      [self.backgroundColor set];


     NSImage * image = ascending ?  [NSImage imageNamed:@"NSDescendingSortIndicator"]:[NSImage imageNamed:@"NSAscendingSortIndicator"] ;
     // use your image here.  If you need to change color, try to make a colored templated image here.

     CGRect frame = [self sortIndicatorRectForBounds:cellFrame];
    [self.backgroundColor set];
    CGRect res = NSMakeRect(frame.origin.x, cellFrame.origin.y,  cellFrame.size.width - frame.origin.x, cellFrame.size.height);
    NSRectFillUsingOperation(res , NSCompositingOperationSourceOver);

    [NSColor.blueColor setFill];
    [NSColor.blueColor setStroke];
    [image drawInRect: [self sortIndicatorRectForBounds:frame]];

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