TableView单元格自定义类型文本重叠本身或为空白

时间:2013-09-03 09:17:14

标签: ios iphone uitableview core-data

希望有人可以告诉我使用自定义标签填充UITableViewCell的正确方法。我一直遇到同样的问题,标签是空白的,或者当表填满cells时,重复使用的标签显示重叠的label文字。

我已经按照这里为其他人提供的建议,但每次我做的事情都会中断。

**更新**

我已将代码更改为以下内容并且大部分代码都工作,除非我选择一个标签重叠的单元格,如下面的屏幕截图所示。我正在使用的教程的代码可以在这里找到:https://github.com/funkyboy/Core-Data-on-iOS-5-Tutorial--How-To-Work-with-Relations-and-Predicates

这是我的代码:

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

if (!cell) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}

FailedBankInfo *info = [_fetchedResultsController objectAtIndexPath:indexPath];

CGRect infoFrame = CGRectMake(10, 10, 200, 16);
UILabel *infoLabel = [[UILabel alloc] initWithFrame:infoFrame];
infoLabel.textColor = [UIColor blackColor];
infoLabel.backgroundColor = [UIColor yellowColor];
infoLabel.font = [UIFont systemFontOfSize:12];
infoLabel.textAlignment = NSTextAlignmentCenter;
infoLabel.tag = indexPath.row;
infoLabel.text = [NSString stringWithFormat:@"%@, %@", info.city, info.state];
[cell addSubview:infoLabel];

return cell;
}

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {

FailedBankInfo *info = [_fetchedResultsController objectAtIndexPath:indexPath];
UILabel *theLabel = (UILabel *)[cell.contentView viewWithTag:indexPath.row];
theLabel.text = [NSString stringWithFormat:@"%@, %@",
                 info.city, info.state];

}

因为我正在使用带有核心数据的原型Cellfetchedresultcontroller永远不会调用if(!cell)。我将标记更改为indexPath.row编号。如果您使用预定义的cell类型,我所遵循的每个教程都可以正常工作。如果我使用自己的标签自定义它永远不会有效。我不应该使用fetchedresultscontroller作为tableview数据吗?如果我在详细视图中更改城市标题并保存它,则会调用以下代码:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {

UITableView *tableView = self.tableView;

switch(type) {

    case NSFetchedResultsChangeInsert:
        [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeDelete:
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeUpdate:
        [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
        break;

    case NSFetchedResultsChangeMove:
        [tableView deleteRowsAtIndexPaths:[NSArray
                                           arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        [tableView insertRowsAtIndexPaths:[NSArray
                                           arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;
    }
}

以下是一些截图:

storyboard

changed cell

selected cell

7 个答案:

答案 0 :(得分:7)

***更新 - 使用自定义单元格时,删除原型单元格,因为您不需要它!

我终于弄明白为什么它不起作用。整个问题是Prototype Cell,因为你必须设置一个重用标识符,在大多数教程中它们使用字符串“Cell”。

当tableview加载时,它已经有一个带有标识符“Cell”的单元格,因此它不会创建一个新的单元格,它只会重用具有标识符“Cell”的单元格。

static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault  reuseIdentifier:CellIdentifier];
}

上面的块if if(cell == nil)从未被调用,因为它正在重用已经存在的Prototype单元格。另一个问题是在此块下方创建标签,如下所示:

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

if (!cell) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
FailedBankInfo *info = [_fetchedResultsController objectAtIndex:indexPath.row];

CGRect infoFrame = CGRectMake(10, 10, 200, 16);
UILabel *infoLabel = [[UILabel alloc] initWithFrame:infoFrame];
infoLabel.textColor = [UIColor blackColor];
infoLabel.backgroundColor = [UIColor yellowColor];
infoLabel.font = [UIFont systemFontOfSize:12];
infoLabel.textAlignment = NSTextAlignmentCenter;
infoLabel.tag = 101;
infoLable.text = [NSString stringWithFormat:@"%@, %@",
         info.city, info.state];
[cell.contentView addSubview:infoLabel];

return cell;
}

这似乎可以正常工作,因为它会创建标签,但是当您滚动屏幕时,您会发现标签与我的问题重叠。这是因为当一个单元格离开屏幕并返回时,cellForRowAtIndexPath会重新使用已有标签的单元格并在顶部创建另一个Label。这种情况一直持续到您在同一个单元格上重叠7,8个或更多标签为止。我通过在didSelectRowatIndex中放置一个NSlog来计算子视图的数量来解决这个问题:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

NSLog(@"Count is:%i", [[tableView cellForRowAtIndexPath:indexPath].contentView.subviews count]);
}

所以你需要做的是在if(cell == nil)中创建自定义Label,并使用else来设置Label的文本。这个工作的关键是每个单元必须具有唯一的重用标识符,因此当调用CellforRowatIndexPath时,它会在第一次出现时创建一个新单元。当该单元格离开屏幕并返回时,新单元格未创建,它只是重用现有单元格并更改文本而不创建另一个标签。我已经用if(cell == nil)中的另一个NSlog检查了这个,它为每个单元格触发一次,然后它不会触发,因为它重用了出队的单元格。我只是使用字符串“myCell”+ indexPath.row作为标识符。在故事板中,我仍然拥有Prototype单元格的“Cell”标识符。如果你在那里没有东西,它会抱怨。这是我的最终代码:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *myCellID = [NSString stringWithFormat:@"myCell%i", indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:myCellID];

FailedBankInfo *info = [_fetchedResultsController objectAtIndexPath:indexPath];

if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:myCellID];
    CGRect infoFrame = CGRectMake(10, 10, 200, 16);
    UILabel *infoLabel = [[UILabel alloc] initWithFrame:infoFrame];
    infoLabel.textColor = [UIColor blackColor];
    infoLabel.backgroundColor = [UIColor yellowColor];
    infoLabel.font = [UIFont systemFontOfSize:12];
    infoLabel.textAlignment = NSTextAlignmentCenter;
    infoLabel.tag = 101;
    infoLabel.text = [NSString stringWithFormat:@"%@", info.name];
    [cell.contentView addSubview:infoLabel];
}
else {
    UILabel *theLabel = (UILabel *)[cell viewWithTag:101];
    theLabel.text = [NSString stringWithFormat:@"%@", info.name];
}

return cell;
}

我还在使用代码:

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {

FailedBankInfo *info = [_fetchedResultsController objectAtIndexPath:indexPath];
UILabel *theLabel = (UILabel *)[cell viewWithTag:101];
theLabel.text = [NSString stringWithFormat:@"%@", info.name];
}

使用以下代码更改Core数据对象时标签文本:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {

UITableView *tableView = self.tableView;

switch(type) {

    case NSFetchedResultsChangeInsert:
        [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeDelete:
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeUpdate:
        [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
        break;

    case NSFetchedResultsChangeMove:
        [tableView deleteRowsAtIndexPaths:[NSArray
                                           arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        [tableView insertRowsAtIndexPaths:[NSArray
                                           arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;
  }
}

我也发现有时候当回到表格视图时表格并不总是更新所以要解决这个问题你可以使用:

-(void) viewWillAppear {
[self.tableView reloadData];
}

感谢您的所有输入,它确实帮助我找到了解决方案,我希望这可以帮助其他遇到同样问题的人。我想我已经解释得很好了。我无论如何都不是专家。只是另一个人试图建立一个应用程序:)

答案 1 :(得分:5)

我只用2-3行代码修复它。发生这种情况是因为当tableView使用此内容显示您的内容时。

[cell.contentView addSubView:myView];

现在,如果你的tableView不是Scrolling,但是当你滚动TableView然后它与内容重叠,它会完美地工作,因为它不会从内存中清除旧内容,你可以在内存调试会话中看到内存使用情况。现在你的内存消耗将随着滚动而增加。

因此,最佳解决方案是在将新内容添加到单元格内存之前,从Cell的contentView First中清除所有旧内容。这样它就会顺利运行。

在将任何内容添加到contentView之前,我使用以下代码清除Cell的contentView中的旧内存。

for (id object in cell.contentView.subviews)
{
    [object removeFromSuperview];
}  

我认为这是完美的解决方案。但我仍然需要你的评论。请分享您的经验。

答案 2 :(得分:1)

对于故事板,创建自定义单元格类如下所示。直观地设置标签并设置所需的属性,如颜色,字体,大小等

myCell.h

#import <UIKit/UIKit.h>

@interface myCell : UITableViewCell

//connect your labels and image as below

@property (weak, nonatomic) IBOutlet UIImageView *myImage;
@property (weak, nonatomic) IBOutlet UILabel *myLabel1;
@property (weak, nonatomic) IBOutlet UILabel *myLabel2;
@property (weak, nonatomic) IBOutlet UILabel *myLabel3;

@end


yourViewController.m

//Set up your custom cell

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

static NSString *cellIdentifier = @"Cell";
myCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

if (cell == nil) {

    cell = [[myCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];

}

cell.myLabel1.text = @"hello1";
cell.myLabel2.text = @"hello2";
cell.myLabel3.text = @"hello3";

[cell.myImage setImage:[UIImage imageNamed:@"google.jpg"]];
return cell;

}

答案 3 :(得分:0)

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

if (!cell) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }
    FailedBankInfo *info = [_fetchedResultsController objectAtIndex:indexPath.row];

    CGRect infoFrame = CGRectMake(10, 10, 200, 16);
    UILabel *infoLabel = [[UILabel alloc] initWithFrame:infoFrame];
    infoLabel.textColor = [UIColor blackColor];
    infoLabel.backgroundColor = [UIColor yellowColor];
    infoLabel.font = [UIFont systemFontOfSize:12];
    infoLabel.textAlignment = NSTextAlignmentCenter;
    infoLabel.tag = 101;
    infoLable.text = [NSString stringWithFormat:@"%@, %@",
             info.city, info.state];
    [cell.contentView addSubview:infoLabel];

    return cell;
}

答案 4 :(得分:0)

我之前曾多次遇到过tableview这样的问题。到目前为止,我找到的最佳解决方案是创建自己的“reuseTableViewCellWithIdentifier”方法(在myViewController.m中),然后使用它来获取“cellForRowAtIndexPath方法”中的单元格。 在你的情况下,它将是这样的:

-(UITableViewCell *)reuseTableViewCellWithIdentifier:(NSString *)identifier withIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell =[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
   FailedBankInfo *info = [_fetchedResultsController objectAtIndex:indexPath.row];
   CGRect infoFrame = CGRectMake(10, 10, 200, 16);
   UILabel *infoLabel = [[UILabel alloc] initWithFrame:infoFrame];
   infoLabel.textColor = [UIColor blackColor];
   infoLabel.backgroundColor = [UIColor yellowColor];
   infoLabel.font = [UIFont systemFontOfSize:12];
   infoLabel.textAlignment = NSTextAlignmentCenter;
   infoLabel.tag = 101;
   infoLabel.text = [NSString stringWithFormat:@"%@, %@", info.city, info.state];
   [cell.contentView addSubview:b];
   return cell;
}

现在您只需要使用其标记获取Label并在您的cellForRow方法中更改其值,如下所示:

  - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (nil == cell) {
        cell = [self reuseTableViewCellWithIdentifier:CellIdentifier withIndexPath:indexPath];
    }
    UILabel *infoLabel = (UILabel *)[cell.contentView viewWithTag:101];
    FailedBankInfo *info = [_fetchedResultsController objectAtIndex:indexPath.row];
    infoLabel.text = [NSString stringWithFormat:@"%@, %@", info.city, info.state];

    return cell;
}

答案 5 :(得分:0)

我有同样的问题。我认为这是因为我们正在重复使用标识符,因此单元格与其他单元格重叠。以下代码行为我工作..

UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:nil];

答案 6 :(得分:0)

您只需勾选清除故事板中该标签的属性部分中的图形上下文。