滚动时UITableView奇怪的行为

时间:2014-10-05 19:19:18

标签: ios cocoa-touch uitableview

我创建了一个UITableView,其中包含显示Users的单元格。在此方法-tableView:cellForRowAtIndexPath:中添加每个单元格。每个单元格都有与特定用户相关联的内容,例如UIImageViewUILabel

只要显示的单元格不超过9-10个,UITableView就能正常工作。但是当细胞数量变得更高时,用户必须向下滚动才能查看它们,这就是奇怪的行为开始时的情况。来自第一个,第二个,第三个等的内容被添加到单元号十一,十二,十三等等。当用户然后向上滚动时,应该在11,12,13号上的内容现在位于第一,第二和第三单元格中......

我希望有人理解我的问题,并知道这里有什么问题..

这是我用户添加单元格的代码..忽略解析的东西虽然,我不认为它是相关的

- (UITableViewCell *)tableView:(UITableView *)tableview cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpleTableIdentifier = @"SimpleTableCell";

UITableViewCell *cell = [tableview dequeueReusableCellWithIdentifier:simpleTableIdentifier];

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

    if (tableview == commentViewTableView) {
        //Ignore this
    } else if (tableview == tableView) {
        UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, 34, 34)];
        imageView.contentMode = UIViewContentModeScaleAspectFill;
        imageView.clipsToBounds = YES;
        [cell addSubview:imageView];

        UILabel *usernameLabel = [[UILabel alloc] initWithFrame:CGRectMake(44, 0, 160, 44)];
        usernameLabel.textAlignment = NSTextAlignmentLeft;
        usernameLabel.font = [UIFont systemFontOfSize:17];
        usernameLabel.backgroundColor = [UIColor clearColor];
        [cell addSubview:usernameLabel];


        UIImageView *hitImageView = [[UIImageView alloc] initWithFrame:CGRectMake(245, 9.5, 25, 25)];
        hitImageView.contentMode = UIViewContentModeScaleAspectFill;
        hitImageView.clipsToBounds = YES;
        hitImageView.image = [UIImage imageNamed:@"hit.png"];
        [cell addSubview:hitImageView];

        NSString *key = //Code to retrieve userKey

        PFQuery *query = [PFUser query];
        [query whereKey:@"objectId" equalTo:key];
        [query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
            if (!error) {
                [[object objectForKey:@"image1"] getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
                        if (!error) {

                            NSString *ageString = [[NSString alloc] initWithFormat:@"%li", (long)age];


                            imageView.image = [UIImage imageWithData:data];
                            usernameLabel.text = [NSString stringWithFormat:@"%@, %@", [object objectForKey:@"username"], ageString];

                        }
                    }];
            }
        }];
    }
}

return cell;

}

3 个答案:

答案 0 :(得分:1)

我通过将单元格标识符更改为唯一来解决了我的问题。我不知道这是否真的是这样做的方式,或者这是不是很好的做法,但是当我这样做时它解决了我的问题。因此,如果知道这是否会导致我可能遗失的任何其他问题,那么反馈会很好吗?

NSString *identifier = [NSString stringWithFormat:@"Cell%li", indexPath.row];
UITableViewCell *cell = [tableview dequeueReusableCellWithIdentifier:identifier];

if (cell == nil) {
    //My code..
}

答案 1 :(得分:0)

更改您的代码:

- (UITableViewCell *)tableView:(UITableView *)tableview cellForRowAtIndexPath:(NSIndexPath *)indexPath {
   static NSString *simpleTableIdentifier = @"SimpleTableCell";

   UITableViewCell *cell = [tableview dequeueReusableCellWithIdentifier:simpleTableIdentifier];

   if (cell == nil) {
      cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
   } // CLOSED PARANTHESES HERE!!!!

   if (tableview == commentViewTableView) {
       //Ignore this
   } else if (tableview == tableView) {
      // ... rest of your code here
   }
 }

答案 2 :(得分:0)

代码存在一些问题。一个是必须特别注意cellForRowAtIndex:datasource方法中的异步调用。另一个是细胞被重复使用,因此每次进入视图时向它们添加子视图将在子视图上堆积子视图。

让我们从异步操作开始。 @nburk正确地指出了这个问题,但是说你不能轻易做到这一点"。您可以预加载所有内容,但是用户必须等待整个表准备好才能看到任何内容。这里的一个好策略是延迟加载。

延迟加载取决于缓存加载结果的位置。因此,让我们的数据源数组成为可变字典的数组,如下所示:

@{@"user": aPFUser, @"image": aUIImage };

预取用户是有意义的,否则,你甚至不知道你有多少,所以,在viewWillAppear:

// setup your model as @property(strong,nonatomic) NSMutableArray *users;
PFQuery *query = [PFUser query];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
    if (!error) {
        // build the datasource
        self.users = [NSMutableArray array];
        for (PFUser *user in objects) {
            NSMutableDictionary *d = [NSMutableDictionary dictionaryWithDictionary:
                @{ @"user": user };
            ];
        }
    [self.tableView reloadData];
    }
}];

现在,在cellForRowAtIndexPath中执行此操作:

NSMutableDictionary *userDictionary = self.users[indexPath.row];

// in the lazy pattern, if the model is initialized, we're done
// start by assuming the best
imageView.image = userDictionary[@"image"];

// but the image might be nil (it will start out that way) so, load...
PFQuery *query = [PFUser query];
[query whereKey:@"objectId" equalTo:key];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
    if (!error) {
        [[object objectForKey:@"image1"] getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
            if (!error) {
                UIImage *image = [UIImage imageWithData:data];

                // this is the important part:  this code doesn't run when the rest
                // of the method runs.  It happens later, when the request completes
                // so don't assume anything about the state of the table.  Instead
                // treat the table like you would in other view controller methods
                userDictionary[@"image"] = image;
                // don't fool around with cell (it might be reused).  Instead 
                // just tell the table to reload this row
                [tableView reloadRowsAtIndexPaths:@[indexPath]
                                 withRowAnimation:UITableViewRowAnimationAutomatic];
            }
        }];
    }
}];

下一次该行滚动到视图中时,来自异步请求的数据将缓存在您的用户词典中。

问题二更简单:代码无条件地构建子视图,即使(重用)单元格已经具有该子视图。答案是,懒惰是你的朋友。尝试从单元格中获取子视图,并且只有在必须时才构建它...

// change all of your subview-building code to do this:
UIImageView *imageView = (UIImageView *)[cell viewWithTag:32];
if (!imageView) {
   imageView = [[UIImageView alloc] init....
   // same code as you had here, adding...
   imageView.tag = 32;
}
// and so on for the cell's other subviews.  be sure to advance the tag (33, 34, etc)

总之,cellForRowAtIndexPath有几个部分。

  • 将单元格出列
  • 上面的lazy-build子视图
  • 如上所述:访问您的模型并乐观地从模型中初始化子视图
  • 如果缺少部分模型,请执行异步调用,更新模型, 完成后重新加载单元格