Como personalizar as colors de fundo / borda de uma célula de exibição de tabela agrupada?

Gostaria de personalizar o plano de fundo e a cor da borda de um UITableView de estilo agrupado.

Consegui personalizar a cor do plano de fundo usando o seguinte:

tableView.contentView.backgroundColor = [UIColor greenColor]; 

Mas a cor da borda ainda é algo que não sei mudar.

Como eu personalizo esses dois aspectos da exibição de tabela de estilo agrupado?

ATUALIZAÇÃO: No iPhone OS 3.0 e posterior, o UITableViewCell agora possui uma propriedade backgroundColor que torna isso realmente fácil (especialmente em combinação com o [UIColor colorWithPatternImage:] ). Mas vou deixar a versão 2.0 da resposta aqui para qualquer um que precise dela …


É mais difícil do que deveria ser. Aqui está como eu fiz isso quando tive que fazer isso:

Você precisa definir a propriedade backgroundView do UITableViewCell como um UIView personalizado que desenhe a borda e o próprio plano de fundo nas colors apropriadas. Essa visualização precisa ser capaz de desenhar as bordas em quatro modos diferentes, arredondados na parte superior da primeira célula de uma seção, arredondados na parte inferior da última célula de uma seção, sem cantos arredondados para células no meio de uma seção e arredondado em todos os 4 cantos para seções que contenham uma célula.

Infelizmente, não consegui descobrir como configurar esse modo automaticamente, então tive que configurá-lo no método -cellForRowAtIndexPath do UITableViewDataSource.

É uma verdadeira PITA, mas confirmei com os engenheiros da Apple que esse é atualmente o único caminho.

Atualização Aqui está o código para essa visualização personalizada. Há um bug de desenho que faz os cantos arredondados parecerem um pouco engraçados, mas nós mudamos para um design diferente e descartamos os planos de fundo personalizados antes que eu tivesse a chance de consertá-lo. Ainda assim, isso provavelmente será muito útil para você:

 // // CustomCellBackgroundView.h // // Created by Mike Akers on 11/21/08. // Copyright 2008 __MyCompanyName__. All rights reserved. // #import  typedef enum { CustomCellBackgroundViewPositionTop, CustomCellBackgroundViewPositionMiddle, CustomCellBackgroundViewPositionBottom, CustomCellBackgroundViewPositionSingle } CustomCellBackgroundViewPosition; @interface CustomCellBackgroundView : UIView { UIColor *borderColor; UIColor *fillColor; CustomCellBackgroundViewPosition position; } @property(nonatomic, retain) UIColor *borderColor, *fillColor; @property(nonatomic) CustomCellBackgroundViewPosition position; @end // // CustomCellBackgroundView.m // // Created by Mike Akers on 11/21/08. // Copyright 2008 __MyCompanyName__. All rights reserved. // #import "CustomCellBackgroundView.h" static void addRoundedRectToPath(CGContextRef context, CGRect rect, float ovalWidth,float ovalHeight); @implementation CustomCellBackgroundView @synthesize borderColor, fillColor, position; - (BOOL) isOpaque { return NO; } - (id)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { // Initialization code } return self; } - (void)drawRect:(CGRect)rect { // Drawing code CGContextRef c = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(c, [fillColor CGColor]); CGContextSetStrokeColorWithColor(c, [borderColor CGColor]); if (position == CustomCellBackgroundViewPositionTop) { CGContextFillRect(c, CGRectMake(0.0f, rect.size.height - 10.0f, rect.size.width, 10.0f)); CGContextBeginPath(c); CGContextMoveToPoint(c, 0.0f, rect.size.height - 10.0f); CGContextAddLineToPoint(c, 0.0f, rect.size.height); CGContextAddLineToPoint(c, rect.size.width, rect.size.height); CGContextAddLineToPoint(c, rect.size.width, rect.size.height - 10.0f); CGContextStrokePath(c); CGContextClipToRect(c, CGRectMake(0.0f, 0.0f, rect.size.width, rect.size.height - 10.0f)); } else if (position == CustomCellBackgroundViewPositionBottom) { CGContextFillRect(c, CGRectMake(0.0f, 0.0f, rect.size.width, 10.0f)); CGContextBeginPath(c); CGContextMoveToPoint(c, 0.0f, 10.0f); CGContextAddLineToPoint(c, 0.0f, 0.0f); CGContextStrokePath(c); CGContextBeginPath(c); CGContextMoveToPoint(c, rect.size.width, 0.0f); CGContextAddLineToPoint(c, rect.size.width, 10.0f); CGContextStrokePath(c); CGContextClipToRect(c, CGRectMake(0.0f, 10.0f, rect.size.width, rect.size.height)); } else if (position == CustomCellBackgroundViewPositionMiddle) { CGContextFillRect(c, rect); CGContextBeginPath(c); CGContextMoveToPoint(c, 0.0f, 0.0f); CGContextAddLineToPoint(c, 0.0f, rect.size.height); CGContextAddLineToPoint(c, rect.size.width, rect.size.height); CGContextAddLineToPoint(c, rect.size.width, 0.0f); CGContextStrokePath(c); return; // no need to bother drawing rounded corners, so we return } // At this point the clip rect is set to only draw the appropriate // corners, so we fill and stroke a rounded rect taking the entire rect CGContextBeginPath(c); addRoundedRectToPath(c, rect, 10.0f, 10.0f); CGContextFillPath(c); CGContextSetLineWidth(c, 1); CGContextBeginPath(c); addRoundedRectToPath(c, rect, 10.0f, 10.0f); CGContextStrokePath(c); } - (void)dealloc { [borderColor release]; [fillColor release]; [super dealloc]; } @end static void addRoundedRectToPath(CGContextRef context, CGRect rect, float ovalWidth,float ovalHeight) { float fw, fh; if (ovalWidth == 0 || ovalHeight == 0) {// 1 CGContextAddRect(context, rect); return; } CGContextSaveGState(context);// 2 CGContextTranslateCTM (context, CGRectGetMinX(rect),// 3 CGRectGetMinY(rect)); CGContextScaleCTM (context, ovalWidth, ovalHeight);// 4 fw = CGRectGetWidth (rect) / ovalWidth;// 5 fh = CGRectGetHeight (rect) / ovalHeight;// 6 CGContextMoveToPoint(context, fw, fh/2); // 7 CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1);// 8 CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1);// 9 CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1);// 10 CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1); // 11 CGContextClosePath(context);// 12 CGContextRestoreGState(context);// 13 } 

Eu sei que as respostas estão relacionadas à mudança de células de tabelas agrupadas, mas no caso de alguém querer alterar também a cor de fundo da tableview:

Você não precisa apenas definir:

 tableview.backgroundColor = color; 

Você também precisa mudar ou se livrar da visão de fundo:

 tableview.backgroundView = nil; 

Primeiramente, obrigado por este código. Eu fiz algumas alterações de desenho nesta function para remover problema de canto do desenho.

 -(void)drawRect:(CGRect)rect { // Drawing code CGContextRef c = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(c, [fillColor CGColor]); CGContextSetStrokeColorWithColor(c, [borderColor CGColor]); CGContextSetLineWidth(c, 2); if (position == CustomCellBackgroundViewPositionTop) { CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ; CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ; minx = minx + 1; miny = miny + 1; maxx = maxx - 1; maxy = maxy ; CGContextMoveToPoint(c, minx, maxy); CGContextAddArcToPoint(c, minx, miny, midx, miny, ROUND_SIZE); CGContextAddArcToPoint(c, maxx, miny, maxx, maxy, ROUND_SIZE); CGContextAddLineToPoint(c, maxx, maxy); // Close the path CGContextClosePath(c); // Fill & stroke the path CGContextDrawPath(c, kCGPathFillStroke); return; } else if (position == CustomCellBackgroundViewPositionBottom) { CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ; CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ; minx = minx + 1; miny = miny ; maxx = maxx - 1; maxy = maxy - 1; CGContextMoveToPoint(c, minx, miny); CGContextAddArcToPoint(c, minx, maxy, midx, maxy, ROUND_SIZE); CGContextAddArcToPoint(c, maxx, maxy, maxx, miny, ROUND_SIZE); CGContextAddLineToPoint(c, maxx, miny); // Close the path CGContextClosePath(c); // Fill & stroke the path CGContextDrawPath(c, kCGPathFillStroke); return; } else if (position == CustomCellBackgroundViewPositionMiddle) { CGFloat minx = CGRectGetMinX(rect) , maxx = CGRectGetMaxX(rect) ; CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ; minx = minx + 1; miny = miny ; maxx = maxx - 1; maxy = maxy ; CGContextMoveToPoint(c, minx, miny); CGContextAddLineToPoint(c, maxx, miny); CGContextAddLineToPoint(c, maxx, maxy); CGContextAddLineToPoint(c, minx, maxy); CGContextClosePath(c); // Fill & stroke the path CGContextDrawPath(c, kCGPathFillStroke); return; } } 

Obrigado pelo código, é exatamente o que eu estava procurando. Eu também adicionei o seguinte código ao código da Vimal, para implementar o caso de uma célula CustomCellBackgroundViewPositionSingle. (Todos os quatro cantos são arredondados.)

 else if (position == CustomCellBackgroundViewPositionSingle) { CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ; CGFloat miny = CGRectGetMinY(rect) , midy = CGRectGetMidY(rect) , maxy = CGRectGetMaxY(rect) ; minx = minx + 1; miny = miny + 1; maxx = maxx - 1; maxy = maxy - 1; CGContextMoveToPoint(c, minx, midy); CGContextAddArcToPoint(c, minx, miny, midx, miny, ROUND_SIZE); CGContextAddArcToPoint(c, maxx, miny, maxx, midy, ROUND_SIZE); CGContextAddArcToPoint(c, maxx, maxy, midx, maxy, ROUND_SIZE); CGContextAddArcToPoint(c, minx, maxy, minx, midy, ROUND_SIZE); // Close the path CGContextClosePath(c); // Fill & stroke the path CGContextDrawPath(c, kCGPathFillStroke); return; } 

Uma coisa que encontrei com o código CustomCellBackgroundView acima, do Mike Akers, pode ser útil para os outros:

cell.backgroundView não é automaticamente redesenhado quando as células são reutilizadas, e as alterações na posição do backgroundView não afetam as células reutilizadas. Isso significa que tabelas longas terão desenhado incorretamente cell.backgroundViews dadas suas posições.

Para corrigir isso sem ter que criar um novo backgroundView toda vez que uma linha é exibida, chame [cell.backgroundView setNeedsDisplay] no final do seu -[UITableViewController tableView:cellForRowAtIndexPath:] . Ou, para uma solução mais reutilizável, substitua o configurador de posição do CustomCellBackgroundView para include um [self setNeedsDisplay] .

Obrigado por este post super útil. No caso de alguém lá fora (como eu!) Quer apenas ter um fundo de célula completamente vazio em vez de personalizá-lo através de imagens / texto / outro conteúdo em IB e não consegue descobrir como diabos se livrar da fronteira burra / padding / fundo, mesmo que você defini-lo para limpar em IB … aqui está o código que eu usei que fez o truque!

 - (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath { static NSString *cellId = @"cellId"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: cellId]; if (cell == nil) { [[NSBundle mainBundle] loadNibNamed:@"EditTableViewCell" owner:self options:nil]; cell = cellIBOutlet; self.cellIBOutlet = nil; } cell.backgroundView = [[[UIView alloc] initWithFrame: CGRectZero] autorelease]; [cell.backgroundView setNeedsDisplay]; ... any other cell customizations ... return cell; } 

Espero que isso ajude alguém! Parece funcionar como um encanto.

Muito obrigado a todos que postaram seu código. Isso é muito útil.

Eu derivado de uma solução semelhante para alterar a cor de destaque para células de exibição de tabela agrupada. Basicamente, o selectedBackgroundView do UITableViewCell (não o backgroundView). Que mesmo no iPhone OS 3.0 ainda precisa desta solução PITA, tanto quanto eu posso dizer …

O código abaixo tem as alterações para renderizar o destaque com um gradiente em vez de uma cor sólida. Além disso, a renderização de borda é removida. Apreciar.

 // // CSCustomCellBackgroundView.h // #import  typedef enum { CustomCellBackgroundViewPositionTop, CustomCellBackgroundViewPositionMiddle, CustomCellBackgroundViewPositionBottom, CustomCellBackgroundViewPositionSingle, CustomCellBackgroundViewPositionPlain } CustomCellBackgroundViewPosition; @interface CSCustomCellBackgroundView : UIView { CustomCellBackgroundViewPosition position; CGGradientRef gradient; } @property(nonatomic) CustomCellBackgroundViewPosition position; @end // // CSCustomCellBackgroundView.m // #import "CSCustomCellBackgroundView.h" #define ROUND_SIZE 10 static void addRoundedRectToPath(CGContextRef context, CGRect rect, float ovalWidth,float ovalHeight); @implementation CSCustomCellBackgroundView @synthesize position; - (BOOL) isOpaque { return NO; } - (id)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { // Initialization code const float* topCol = CGColorGetComponents([[UIColor redColor] CGColor]); const float* bottomCol = CGColorGetComponents([[UIColor blueColor] CGColor]); CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); /* CGFloat colors[] = { 5.0 / 255.0, 140.0 / 255.0, 245.0 / 255.0, 1.00, 1.0 / 255.0, 93.0 / 255.0, 230.0 / 255.0, 1.00, };*/ CGFloat colors[]= { topCol[0], topCol[1], topCol[2], topCol[3], bottomCol[0], bottomCol[1], bottomCol[2], bottomCol[3] }; gradient = CGGradientCreateWithColorComponents(rgb, colors, NULL, sizeof(colors)/(sizeof(colors[0])*4)); CGColorSpaceRelease(rgb); } return self; } -(void)drawRect:(CGRect)rect { // Drawing code CGContextRef c = UIGraphicsGetCurrentContext(); if (position == CustomCellBackgroundViewPositionTop) { CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ; CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ; minx = minx + 1; miny = miny + 1; maxx = maxx - 1; maxy = maxy ; CGContextMoveToPoint(c, minx, maxy); CGContextAddArcToPoint(c, minx, miny, midx, miny, ROUND_SIZE); CGContextAddArcToPoint(c, maxx, miny, maxx, maxy, ROUND_SIZE); CGContextAddLineToPoint(c, maxx, maxy); // Close the path CGContextClosePath(c); CGContextSaveGState(c); CGContextClip(c); CGContextDrawLinearGradient(c, gradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); CGContextRestoreGState(c); return; } else if (position == CustomCellBackgroundViewPositionBottom) { CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ; CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ; minx = minx + 1; miny = miny + 1; maxx = maxx - 1; maxy = maxy - 1; CGContextMoveToPoint(c, minx, miny); CGContextAddArcToPoint(c, minx, maxy, midx, maxy, ROUND_SIZE); CGContextAddArcToPoint(c, maxx, maxy, maxx, miny, ROUND_SIZE); CGContextAddLineToPoint(c, maxx, miny); // Close the path CGContextClosePath(c); CGContextSaveGState(c); CGContextClip(c); CGContextDrawLinearGradient(c, gradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); CGContextRestoreGState(c); return; } else if (position == CustomCellBackgroundViewPositionMiddle) { CGFloat minx = CGRectGetMinX(rect) , maxx = CGRectGetMaxX(rect) ; CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ; minx = minx + 1; miny = miny + 1; maxx = maxx - 1; maxy = maxy ; CGContextMoveToPoint(c, minx, miny); CGContextAddLineToPoint(c, maxx, miny); CGContextAddLineToPoint(c, maxx, maxy); CGContextAddLineToPoint(c, minx, maxy); // Close the path CGContextClosePath(c); CGContextSaveGState(c); CGContextClip(c); CGContextDrawLinearGradient(c, gradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); CGContextRestoreGState(c); return; } else if (position == CustomCellBackgroundViewPositionSingle) { CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ; CGFloat miny = CGRectGetMinY(rect) , midy = CGRectGetMidY(rect) , maxy = CGRectGetMaxY(rect) ; minx = minx + 1; miny = miny + 1; maxx = maxx - 1; maxy = maxy - 1; CGContextMoveToPoint(c, minx, midy); CGContextAddArcToPoint(c, minx, miny, midx, miny, ROUND_SIZE); CGContextAddArcToPoint(c, maxx, miny, maxx, midy, ROUND_SIZE); CGContextAddArcToPoint(c, maxx, maxy, midx, maxy, ROUND_SIZE); CGContextAddArcToPoint(c, minx, maxy, minx, midy, ROUND_SIZE); // Close the path CGContextClosePath(c); CGContextSaveGState(c); CGContextClip(c); CGContextDrawLinearGradient(c, gradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); CGContextRestoreGState(c); return; } else if (position == CustomCellBackgroundViewPositionPlain) { CGFloat minx = CGRectGetMinX(rect); CGFloat miny = CGRectGetMinY(rect), maxy = CGRectGetMaxY(rect) ; CGContextDrawLinearGradient(c, gradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); return; } } - (void)dealloc { CGGradientRelease(gradient); [super dealloc]; } - (void) setPosition:(CustomCellBackgroundViewPosition)inPosition { if(position != inPosition) { position = inPosition; [self setNeedsDisplay]; } } @end static void addRoundedRectToPath(CGContextRef context, CGRect rect, float ovalWidth,float ovalHeight) { float fw, fh; if (ovalWidth == 0 || ovalHeight == 0) {// 1 CGContextAddRect(context, rect); return; } CGContextSaveGState(context);// 2 CGContextTranslateCTM (context, CGRectGetMinX(rect),// 3 CGRectGetMinY(rect)); CGContextScaleCTM (context, ovalWidth, ovalHeight);// 4 fw = CGRectGetWidth (rect) / ovalWidth;// 5 fh = CGRectGetHeight (rect) / ovalHeight;// 6 CGContextMoveToPoint(context, fw, fh/2); // 7 CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1);// 8 CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1);// 9 CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1);// 10 CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1); // 11 CGContextClosePath(context);// 12 CGContextRestoreGState(context);// 13 } 

Você pode personalizar a cor da borda definindo

 tableView.separatorColor 

Para alterar a cor da borda da exibição de tabela:

In.h:

 #import  

Em m:

 tableView.layer.masksToBounds=YES; tableView.layer.borderWidth = 1.0f; tableView.layer.borderColor = [UIColor whiteColor].CGColor; 

Esta tarefa pode ser facilmente feita usando PrettyKit , adicionando cerca de 5 linhas de código. Se você usar arquivos nib ou storyboard , também não se esqueça de aplicar este pequeno hack . Quando você usa essa abordagem, você deve subclass seu celular de PrettyTableViewCell :

 #import  @class RRSearchHistoryItem; @interface RRSearchHistoryCell : PrettyTableViewCell 

Este é o exemplo do meu cellForRowAtIndexPath :

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellIdentifier = @"RRSearchHistoryCell"; RRSearchHistoryCell *cell = (RRSearchHistoryCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if ( cell == nil ) { NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"RRSearchHistoryCell" owner:self options:nil]; cell = topLevelObjects[0]; cell.gradientStartColor = RGB(0xffffff); cell.gradientEndColor = RGB(0xf3f3f3); } RRSearchHistoryItem *item = _historyTableData[indexPath.row]; [cell setHistoryItem:item]; [cell prepareForTableView:tableView indexPath:indexPath]; return cell; } 

Eu tenho tido problemas com isso e tentei muitas combinações de coisas, como notei que para algumas células funcionou bem, mas não para os outros.

Estranhamente eu descobri que é possível configurar o cell.backgroundColor para lightGrayColor e tudo funciona perfeitamente – mas blueColor me causou problemas ao não atualizar as bordas externas.

A menos que seja realmente importante usar verde – talvez você queira tentar isso. Pode ser que esse seja um recurso para levar as pessoas a usar apenas colors cinza ao indicar que uma célula está selecionada.