5

这是我设置自定义分组表视图单元格背景的解决方案:

- (UIView *)top
{
    if (_top) {
        return _top;
    }

    _top = [[UIView alloc] init];
    [_top setBackgroundColor:[UIColor blueColor]];

    return _top;
}

// dot dot dot

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSInteger section = [indexPath section];
    NSInteger row = [indexPath row];
    NSInteger maxRow = [tableView numberOfRowsInSection:section] - 1;

    if (maxRow == 0) {
        [cell setBackgroundView:[self lonely]];
    } else if (row == 0) {
        [cell setBackgroundView:[self top]];
    } else if (row == maxRow) {
        [cell setBackgroundView:[self bottom]];
    } else {
        [cell setBackgroundView:[self middle]];
    }
}

显然它没有按预期工作,这将我带到这里,但是当我使用缓存视图时它确实有效:

UIView *background = [[UIView alloc] init];

if (maxRow == 0) {
    [background setBackgroundColor:[UIColor redColor]];
} else if (row == 0) {
    [background setBackgroundColor:[UIColor blueColor]];
} else if (row == maxRow) {
    [background setBackgroundColor:[UIColor yellowColor]];
} else {
    [background setBackgroundColor:[UIColor greenColor]];
}

[cell setBackgroundView:background];

更新:在乔纳森指出我不能对多个单元格使用同一个视图之后,我决定遵循表格视图模型,它有一个可重复使用的单元格队列。对于我的实现,我有一个可重复使用的背景视图队列 (_backgroundViewPool):

@implementation RootViewController {
    NSMutableSet *_backgroundViewPool;
}
- (id)initWithStyle:(UITableViewStyle)style
{
    if (self = [super initWithStyle:style]) {
        _backgroundViewPool = [[NSMutableSet alloc] init];

        UITableView *tableView = [self tableView];
        [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Cell"];
    }

    return self;
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 6;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.

    if (section == 0) {
        return 1;
    }

    return 10;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
    [[cell textLabel] setText:[NSString stringWithFormat:@"[%d, %d]", [indexPath section], [indexPath row]]];

    return cell;
}

#pragma mark - Table view delegate

- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    UIView *backgroundView = [cell backgroundView];
    [_backgroundViewPool addObject:backgroundView];
}

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSInteger section = [indexPath section];
    NSInteger row = [indexPath row];
    NSInteger maxRow = [tableView numberOfRowsInSection:section] - 1;
    UIColor *color = nil;


    if (maxRow == 0) {
        // single cell
        color = [UIColor blueColor];
    } else if (row == 0) {
        // top cell
        color = [UIColor redColor];
    } else if (row == maxRow) {
        // bottom cell
        color = [UIColor greenColor];
    } else {
        // middle cell
        color = [UIColor yellowColor];
    }

    UIView *backgroundView = nil;

    for (UIView *bg in _backgroundViewPool) {
        if (color == [bg backgroundColor]) {
            backgroundView = bg;
            break;
        }
    }

    if (backgroundView) {
        [backgroundView retain];
        [_backgroundViewPool removeObject:backgroundView];
    } else {
        backgroundView = [[UIView alloc] init];
        [backgroundView setBackgroundColor:color];
    }

    [cell setBackgroundView:[backgroundView autorelease]];
}

除非您滚动得非常快,否则它可以工作。一些背景视图消失了!我怀疑背景视图仍在多个单元格中使用,但我真的不知道发生了什么,因为一旦重复使用背景视图就应该从队列中删除,从而无法使用背景视图在多个可见单元格中。


自从我发布这个问题以来,我一直在研究这个问题。当前在线分组表格视图单元格的自定义背景视图的解决方案不能令人满意,它们不使用缓存视图。此外,我不想使用 XJones 和 jszumski 提出的解决方案,因为一旦考虑到可重用的自定义单元格(例如,文本字段单元格、开关单元格、滑块单元格),它就会变得很麻烦。

4

9 回答 9

5

您是否考虑过对“孤独”、“顶部”、“底部”和“中间”情况使用 4 个单独的单元标识符,并backgroundView在初始化单元时仅设置一次?这样做可以让您利用UITableView自己的缓存和重用,而无需必须在其之上编写一个实现。


更新:一个分组UITableViewController子类的实现,它使用最少数量的单元重用标识符重用背景视图(Espresso 的用例)。 tableView:willDisplayCell:forRowAtIndexPath:tableView:didDisplayCell:forRowAtIndexPath:执行繁重的工作以应用或回收每个背景视图,并且池化逻辑在backgroundViewForStyle:.

typedef NS_ENUM(NSInteger, JSCellBackgroundStyle) {
    JSCellBackgroundStyleTop = 0,
    JSCellBackgroundStyleMiddle,
    JSCellBackgroundStyleBottom,
    JSCellBackgroundStyleSolitary
};

@implementation JSMasterViewController {
    NSArray *backgroundViewPool;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    // these mutable arrays will be indexed by JSCellBackgroundStyle values
    backgroundViewPool = @[[NSMutableArray array],  // for JSCellBackgroundStyleTop
                           [NSMutableArray array],  // for JSCellBackgroundStyleMiddle
                           [NSMutableArray array],  // for JSCellBackgroundStyleBottom
                           [NSMutableArray array]]; // for JSCellBackgroundStyleSolitary
}


#pragma mark - Table View

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 5;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (section == 2) {
        return 1;

    } else if (section == 3) {
        return 0;
    }

    return 5;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSInteger section = indexPath.section;
    NSInteger row = indexPath.row;

    static NSString *switchCellIdentifier = @"switchCell";
    static NSString *textFieldCellIdentifier = @"fieldCell";
    static NSString *textCellIdentifier = @"textCell";

    UITableViewCell *cell = nil;

    // apply a cached cell type (you would use your own logic to choose types of course)
    if (row % 3 == 0) {
        cell = [tableView dequeueReusableCellWithIdentifier:switchCellIdentifier];

        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:switchCellIdentifier];

            UISwitch *someSwitch = [[UISwitch alloc] init];
            cell.accessoryView = someSwitch;

            cell.textLabel.text = @"Switch Cell";
        }

    } else if (row % 3 == 1) {
        cell = [tableView dequeueReusableCellWithIdentifier:textFieldCellIdentifier];

        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:textFieldCellIdentifier];

            UITextField *someField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 80, 30)];
            someField.borderStyle = UITextBorderStyleRoundedRect;
            cell.accessoryView = someField;

            cell.textLabel.text = @"Field Cell";
        }

    } else {
        cell = [tableView dequeueReusableCellWithIdentifier:textCellIdentifier];

        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:textCellIdentifier];

            cell.textLabel.text = @"Generic Label Cell";
        }
    }

    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    cell.textLabel.backgroundColor = [UIColor clearColor];
    cell.detailTextLabel.text = [NSString stringWithFormat:@"[%d, %d]", section, row];
    cell.detailTextLabel.backgroundColor = [UIColor clearColor];

    return cell;
}

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    // apply a cached background view
    JSCellBackgroundStyle backgroundStyle = [self backgroundStyleForIndexPath:indexPath tableView:tableView];
    cell.backgroundView = [self backgroundViewForStyle:backgroundStyle];
}

- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    JSCellBackgroundStyle backgroundStyle = [self backgroundStyleForIndexPath:indexPath tableView:tableView];
    NSMutableArray *stylePool = backgroundViewPool[backgroundStyle];

    // reclaim the background view for the reuse pool
    [cell.backgroundView removeFromSuperview];

            if (cell.backgroundView != nil) {
            [stylePool addObject:cell.backgroundView];
            }

    cell.backgroundView = nil; // omitting this line will cause some rows to appear without a background because they try to be in two superviews at once
}

- (JSCellBackgroundStyle)backgroundStyleForIndexPath:(NSIndexPath*)indexPath tableView:(UITableView*)tableView {
    NSInteger maxRow = MAX(0, [tableView numberOfRowsInSection:indexPath.section] - 1); // catch the case of a section with 0 rows

    if (maxRow == 0) {
        return JSCellBackgroundStyleSolitary;

    } else if (indexPath.row == 0) {
        return JSCellBackgroundStyleTop;

    } else if (indexPath.row == maxRow) {
        return JSCellBackgroundStyleBottom;

    } else {
        return JSCellBackgroundStyleMiddle;
    }
}

- (UIView*)backgroundViewForStyle:(JSCellBackgroundStyle)style {
    NSMutableArray *stylePool = backgroundViewPool[style];

    // if we have a reusable view available, remove it from the pool and return it
    if ([stylePool count] > 0) {
        UIView *reusableView = stylePool[0];
        [stylePool removeObject:reusableView];

        return reusableView;

    // if we don't have any reusable views, make a new one and return it
    } else {
        UIView *newView = [[UIView alloc] init];

        NSLog(@"Created a new view for style %i", style);

        switch (style) {
            case JSCellBackgroundStyleTop:
                newView.backgroundColor = [UIColor blueColor];
                break;

            case JSCellBackgroundStyleMiddle:
                newView.backgroundColor = [UIColor greenColor];
                break;

            case JSCellBackgroundStyleBottom:
                newView.backgroundColor = [UIColor yellowColor];
                break;

            case JSCellBackgroundStyleSolitary:
                newView.backgroundColor = [UIColor redColor];
                break;
        }

        return newView;
    }
}

@end

尽管您可以很容易地将所有视图转储到一个重用池中,但它会使一些循环逻辑复杂化,并且这种方式更容易理解。

于 2013-04-17T02:52:23.437 回答
1

首先,我会检查为什么需要这种缓存。如果这是一个性能问题,我会检查问题确实是视图,而不是像太多混合层这样的其他问题!

关于缓存,有几种方法。至少想到三个:

  1. 对于四个背景中的每一个,注册一个自己的单元重用标识符。然后根据重用标识符设置背景视图。
  2. 为背景视图使用自己的缓存,并从那里重用背景视图。
  3. 对所有单元格的背景视图使用相同的类,并仅在它们上设置内容。

第一个解决方案很容易实现,但它存在UITableView最终持有大量不需要的单元用于重用的风险。此外,如果您需要更多类型的单元格,则必须为每种类型/背景组合提供单元格。

虽然第二种解决方案重用了单元格背景,但您必须为它们编写自己的缓存,并在必要时设置/取消设置背景。

第三种解决方案仅在背景视图可以配置为显示相应单元格的背景时才有效。它只会重用内容,而不是背景视图本身。

这是第二个解决方案测试的早期屏幕截图:

显示自定义分组表视图的屏幕截图

这是实现:

@implementation RootViewController
{
    NSMutableDictionary *_backgroundViews;
}

- (void)viewDidLoad
{
    _backgroundViews = [NSMutableDictionary dictionary];
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Cell"];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 100;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return section / 10 + 1;
}

- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    cell.backgroundView = nil;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    cell.textLabel.backgroundColor = [UIColor clearColor];
    if (!cell.backgroundView || ![cell.backgroundView isKindOfClass:[UIImageView class]]) {
        NSInteger section = [indexPath section];
        NSInteger row = [indexPath row];
        NSInteger maxRow = [tableView numberOfRowsInSection:section] - 1;
        NSString *imageName = nil;
        UIEdgeInsets insets = UIEdgeInsetsZero;
        if (maxRow == 0) {
            // single cell
            imageName = @"singlebackground";
            insets = UIEdgeInsetsMake(12, 12, 12, 12);
        } else if (row == 0) {
            // top cell
            imageName = @"topbackground";
            insets = UIEdgeInsetsMake(12, 12, 0, 12);
        } else if (row == maxRow) {
            // bottom cell
            imageName = @"bottombackground";
            insets = UIEdgeInsetsMake(0, 12, 12, 12);
        } else {
            // middle cell
            imageName = @"middlebackground";
            insets = UIEdgeInsetsMake(0, 12, 0, 12);
        }

        NSMutableSet *backgrounds = [_backgroundViews objectForKey:imageName];
        if (backgrounds == nil) {
            backgrounds = [NSMutableSet set];
            [_backgroundViews setObject:backgrounds forKey:imageName];
        }

        UIImageView *backgroundView = nil;
        for (UIImageView *candidate in backgrounds) {
            if (candidate.superview == nil) {
                backgroundView = candidate;
                break;
            }
        }
        if (backgroundView == nil) {
            backgroundView = [[UIImageView alloc] init];
            backgroundView.image = [[UIImage imageNamed:imageName] resizableImageWithCapInsets:insets];
            backgroundView.backgroundColor = [UIColor whiteColor];
            backgroundView.opaque = YES;
        }
        cell.backgroundView = backgroundView;
    }

    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
    [[cell textLabel] setText:[NSString stringWithFormat:@"[%d, %d]", [indexPath section], [indexPath row]]];

    return cell;
}

如果您想查看它,这里是我使用的图像(仅非视网膜且太大,但嘿,这只是一个示例):

  • 单一背景.png:

    单一背景.png

  • 顶部背景.png:

    顶部背景.png

  • 中间背景.png:

    中间背景.png

  • 底部背景.png:

    底部背景.png

于 2013-04-23T15:21:38.110 回答
0

编辑 - 使用图像作为背景视图

鉴于您对我的回答的评论,您似乎想在单元格的背景视图中显示图像。目前尚不清楚这些图像是作为资源编译到您的应用程序中还是从服务中下载的。UIImage无论如何,您可以在多个实例中使用同一个UIImageView实例。因此,当您创建单元格时,您可以动态创建一个新UIImageView的用作背景视图,然后根据单元格的 indexPath将image属性设置为适当的。UIImage

如果图像被编译到您的应用程序中,则[UIImage imageNamed:@""]使用 iOS 实现的缓存并且性能会很好。如果您正在下载图像(可能在后台线程上),那么您将需要为图像数据实现磁盘和/或内存缓存。

原始答案

当您将单元格配置为tableView:cellForRowAtIndexPath:使用中时,单元格标识符以使用内置缓存tableView来缓存具有各种背景视图的单元格。

就像是:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *singleCellID = @"single";
    static NSString *firstCellID = @"first";
    static NSString *middleCellID = @"middle";
    static NSString *lastCellID = @"last";

    NSString *cellID = nil;

    NSInteger section = [indexPath section];
    NSInteger row = [indexPath row];
    NSInteger maxRow = [tableView numberOfRowsInSection:section] - 1;
    UIColor *color = nil;

    if (maxRow == 0) {
        // single cell
        cellID = singleCellID;
    } else if (row == 0) {
        // top cell
        cellID = firstCellID;
    } else if (row == maxRow) {
        // bottom cell
        cellID = lastCellID;
    } else {
        // middle cell
        cellID = middleCellID;
   }

   UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath];

   if (cell == nil) {
       if (cellID == singleCellID) {
           // create single cell
           cell = ...
           cell.backgroundView = ...
       }
       else if (cellID == firstCellID) {
           // create first cell
           cell = ...
           cell.backgroundView = ...
       }
       else if (cellID == lastCellID) {
           // create last cell
           cell = ...
           cell.backgroundView = ...
       }
       else {
           // create middle cell
           cell = ...
           cell.backgroundView = ...
       }
   }
}
于 2013-04-17T02:50:39.767 回答
0

[编辑]好的,所以,就您使用自定义背景视图而言,我认为您应该在tableView:cellForRowAtIndexPath:方法中将背景视图分配给单元格的 .backgroundView 属性,并且不要使用您自己的视图缓存机制,因为表格视图会缓存整个单元格它是子视图 - 您在创建单元格时分配背景视图,稍后只需使用适当的值更新它的背景颜色(在您的情况下,基于索引路径)。

此外,这只是一个建议,您的背景视图可能会被单元格的其他内容遮盖(例如,您向 .contentView 添加了一些内容) - 尝试将单元格/contentView .alpha 值设置为 0.5 以便能够看穿它。代码仍然相关 - 每次 UITableView 需要新单元格在屏幕上显示时调用此方法

- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell* cell = nil;
    static NSString* identifer = @"Cell";
    cell = [tableView dequeueReusableCellWithIdentifier:identifer];
    if(cell==nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifer];
        cell.backgroundView = [YourCustomView new];//assign your custom background view here
    }
    cell.textLabel.text = [NSString stringWithFormat:@"%d",indexPath.row];
    //update background view's color based on index path row
    if(indexPath.row==0)
        cell.backgroundView.backgroundColor = [UIColor redColor];
    else if(indexPath.row==1)
        cell.backgroundView.backgroundColor = [UIColor yellowColor];
    else
        cell.backgroundView.backgroundColor = [UIColor blueColor];
    return cell;
}
于 2013-04-17T05:35:56.340 回答
0

自从我使用 tableviews 以来已经有一段时间了,但我隐约记得遇到过这个问题。我相信对 tableView:willDisplayCell:forRowAtIndexPath: 方法的调用是线程化的。当用户滚动速度非常快时,可以同时拨打多个电话。在这种情况下,给定您当前的代码,可能会为多个单元格分配相同的视图,这将导致出现空格。

如果你使用@synchronized(anObject){} 来防止多个线程同时运行相同的代码,你应该能够防止这个问题。

@synchronized (self) {
    UIView *backgroundView = nil;

    for (UIView *bg in _backgroundViewPool) {
        if (color == [bg backgroundColor]) {
            backgroundView = bg;
            break;
        }
    }

    if (backgroundView) {
        [backgroundView retain];
        [_backgroundViewPool removeObject:backgroundView];
    } else {
        backgroundView = [[UIView alloc] init];
        [backgroundView setBackgroundColor:color];
    }
}
于 2013-04-23T02:05:48.923 回答
0

根据我对苹果文档的理解,当一个单元格列时,它仍然具有您之前设置的所有视图和设置。

因此,如果您将背景视图设置为单元格,则它在出队时仍会存在,如果它是新单元格,则不会有背景视图。

我相信您 不需要后台视图池,因为操作系统会为您处理它,因此您可以在重用单元格时重用 BG视图并willDisplayCell:仅在

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
  NSInteger section = [indexPath section];
  NSInteger row = [indexPath row];
  NSInteger maxRow = [tableView numberOfRowsInSection:section] - 1;
  UIColor *color = nil;


  if (maxRow == 0) {
    // single cell
    color = [UIColor blueColor];
  } else if (row == 0) {
    // top cell
    color = [UIColor redColor];
  } else if (row == maxRow) {
    // bottom cell
    color = [UIColor greenColor];
  } else {
    // middle cell
    color = [UIColor yellowColor];
  }

  UIView *backgroundView = nil;

  //***This is the different part***//

  if (cell.backgroundView != nil) {
    NSLog(@"Old Cell, reuse BG View");
    backgroundView = cell.backgroundView;
  } else {
    NSLog(@"New Cell, Create New BG View");
    backgroundView = [[UIView alloc] init];
    [cell setBackgroundView:[backgroundView autorelease]];
  }

  [backgroundView setBackgroundColor:color];  
}

像这样也不需要代码didEndDisplayingCell:

于 2013-04-22T16:11:48.673 回答
0

您不能同时使用两次视图,当您有超过 3 个单元格时会发生这种情况。表的重用机制应该足够了。

我不确定你为什么要从单元格中单独处理背景视图。无论如何,我更改了您的代码,以便没有缺少 backgroundViews 的错误:

笔记!我确实使用了ARC。

static NSString *identifierSingle = @"single";
static NSString *identifierTop = @"top";
static NSString *identifierBtm = @"btm";
static NSString *identifierMid = @"mid";


@implementation RootViewController {
    NSMutableDictionary *_backgroundViewPool;
}

- (id)initWithStyle:(UITableViewStyle)style
{
    if (self = [super initWithStyle:style]) {
        _backgroundViewPool = [[NSMutableDictionary alloc] init];

        UITableView *tableView = [self tableView];
        [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
    }

    return self;
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 6;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.

    if (section == 0) {
        return 1;
    }

    return 10;
}

- (NSString *)tableView:(UITableView *)tableView identifierForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSInteger section = [indexPath section];
    NSInteger row = [indexPath row];
    NSInteger maxRow = [tableView numberOfRowsInSection:section] - 1;

    if (maxRow == 0) {
        // single cell
        return identifierSingle;
    } else if (row == 0) {
        // top cell
        return identifierTop;
    } else if (row == maxRow) {
        // bottom cell
        return identifierBtm;
    } else {
        // middle cell
        return identifierMid;
    }
}

- (UIColor *)tableView:(UITableView *)tableView colorForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSInteger section = [indexPath section];
    NSInteger row = [indexPath row];
    NSInteger maxRow = [tableView numberOfRowsInSection:section] - 1;
    UIColor *color = nil;


    if (maxRow == 0) {
        // single cell
        color = [UIColor blueColor];
    } else if (row == 0) {
        // top cell
        color = [UIColor redColor];
    } else if (row == maxRow) {
        // bottom cell
        color = [UIColor greenColor];
    } else {
        // middle cell
        color = [UIColor yellowColor];
    }
    return color;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *colorIdentifier = [self tableView:tableView identifierForRowAtIndexPath:indexPath];
    NSString *CellIdentifier = @"cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
    [[cell textLabel] setText:[NSString stringWithFormat:@"[%d, %d]", [indexPath section], [indexPath row]]];
    [[cell textLabel] setBackgroundColor:[UIColor clearColor]];

    NSMutableSet *set = [self backgroundPoolForIdentifier:colorIdentifier];
    UIView *backgroundView = [set anyObject];;

    if (backgroundView) {
        [set removeObject:backgroundView];
    } else {
        backgroundView = [[UIView alloc] init];
        [backgroundView setBackgroundColor:[self tableView:tableView colorForRowAtIndexPath:indexPath]];
    }

    [cell setBackgroundView:backgroundView];

    return cell;
}

#pragma mark - Table view delegate

- (NSMutableSet *)backgroundPoolForIdentifier:(NSString *)identifier {
    NSMutableSet *set = [_backgroundViewPool valueForKey:identifier];
    if (!set) {
        set = [[NSMutableSet alloc] init];
        [_backgroundViewPool setValue:set forKey:identifier];
    }
    return set;
}

- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    [[self backgroundPoolForIdentifier:cell.reuseIdentifier] addObject:cell.backgroundView];
}

@end
于 2013-04-13T17:32:16.857 回答
0

您的原始实现不起作用,因为cellForRowAtIndexPath:您有时会返回一个 nil 对象。UITableView 框架然后将该 *cell 对象传递给willDisplayCell:(UITableViewCell*).

ie: 

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
....
return cell;

// cell maybe nil

如果您确实更喜欢使用自己的缓存机制,则可以简单地返回一个普通的 UITableViewCell 对象,如果可用则出队,或者如果没有可重用的对象,则创建一个新对象。

ie:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

  static NSString *CellIdentifier = @"Cell";
  return [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath] 
        || [UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]
        ;
}

然后,“视图只能添加到 1 个超级视图限制”导致您的缓存视图出现跳跃。

于 2013-04-22T14:52:08.417 回答
0

尝试了几件事来做到这一点,但最终对这个非常基本的解决方案感到满意,我知道这并不是一个真正迷人的解决方案,但它给了我流畅的滚动,如果你愿意,你可以试试这个:

NSMutableArray *_viewArray;
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    #define kTotalNoOfRows 1000
        _viewArray = [[NSMutableArray alloc] initWithCapacity:kTotalNoOfRows];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
   for (int i = 0; i < kTotalNoOfRows; i++) {
        UIView * backGroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];

        if (kTotalNoOfRows == 0)
            [backGroundView setBackgroundColor:[UIColor redColor]];
        else if (i == 0)
            [backGroundView setBackgroundColor:[UIColor greenColor]];
        else if (i == (kTotalNoOfRows - 1))
            [backGroundView setBackgroundColor:[UIColor blueColor]];
        else
            [backGroundView setBackgroundColor:[UIColor yellowColor]];

        [_viewArray addObject:backGroundView];        
    }

    return kTotalNoOfRows;
}


- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell* cell = nil;
    static NSString* middleCell = @"middleCell";
    cell = [tableView dequeueReusableCellWithIdentifier:middleCell];
    if(cell==nil) {
        NSInteger maxRow = [tableView numberOfRowsInSection:indexPath.section] - 1;
        if (maxRow != 0 && indexPath.row != 0 && indexPath.row != maxRow) {
            middleCell = nil;
        }
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:middleCell];
        cell.backgroundView = [_viewArray objectAtIndex:indexPath.row];//assign your custom background view here
        [cell.textLabel setBackgroundColor:[UIColor clearColor]];
    }
    cell.textLabel.text = [NSString stringWithFormat:@"%d",indexPath.row];
    return cell;
}

另外我想提一下我到这里的旅程;所以我尝试过的是

  • 创建了一个视图字典:

    UIView * _topView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
    UIView * _bottomView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
    UIView * _middleView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
    UIView * _lonelyView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
    [_topView setBackgroundColor:[UIColor redColor]];
    [_bottomView setBackgroundColor:[UIColor greenColor]];
    [_middleView setBackgroundColor:[UIColor blueColor]];
    [_lonelyView setBackgroundColor:[UIColor yellowColor]];
    _viewDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                     _topView, @"topView",
                                     _bottomView, @"bottomView",
                                     _middleView, @"middleView",
                                     _lonelyView, @"lonelyView", nil];
    
  • 使用 unarchiver 返回这些视图的副本

    - (UIView *) getBackgroundViewWith : (NSInteger) maxRow currentRow : (NSInteger) row{
        if (maxRow == 0) {
            return (UIView *)[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:[_viewDictionary valueForKey:@"lonelyView"]]];//[[_viewDictionary valueForKey:@"lonelyView"] copy];
        } else if (row == 0) {
            return (UIView *)[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:[_viewDictionary valueForKey:@"topView"]]];//[[_viewDictionary valueForKey:@"topView"] copy];
        } else if (row == maxRow) {
            return (UIView *)[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:[_viewDictionary valueForKey:@"bottomView"]]];//[[_viewDictionary valueForKey:@"bottomView"] copy];
        } else {
            return (UIView *)[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:[_viewDictionary valueForKey:@"middleView"]]];//[[_viewDictionary valueForKey:@"middleView"] copy];
        }
        return nil;
    }
    

但它崩溃了,同时用SIGBART. 就这样放弃了。

于 2013-04-22T18:11:26.743 回答