UITableView 在运行时切换到不同的 CustomCells

Posted

技术标签:

【中文标题】UITableView 在运行时切换到不同的 CustomCells【英文标题】:UITableView switching to different CustomCells in runtime 【发布时间】:2014-05-10 16:02:00 【问题描述】:

我有一个UITableView,有两个部分,第二个部分在其标题中包含一个UISegmentedControl。我希望用户可以通过在UISegmentedControl 中选择一个段来更改表格视图内容。我的TableView 还通过呈现我使用 NIB 创建的不同自定义单元来更改单元格的外观。我的一个要求是记住我的表格视图在我的表格视图中加载数据的滚动位置,这样当用户点击我的段控件以更改表格视图内容并再次点击上一个段时,我的表格视图将能够呈现具有用户记忆的滚动位置的数据。

代码:

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    return 2;

-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
    if (section == kUserCoverSection) 
        return 0;
    
    return 30;



-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    if (section == kUserCoverSection) 
        return 1;
    
    switch (self.userAchievementsType) 
        case kUserSilverCollections:
        
            return [userSilverArray count];
        
            break;
        case kUserBronzeCollections:
        
            return [userBronzeArray count];
        
            break;
        case kUserGoldsCollections:
            return [userGoldsArray count];
        
            break;

        default: return 0;
            break;
    


-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    if (indexPath.section == kUserCoverSection) 
        return 145;
    else
        switch (self.userAchievementsType) 
            case kUserSilverCollections:
            case kUserBronzeCollections:
            
                return 40;
            
                break;
            case kUserGoldsCollections:
                return 358;
            
                break;
            default:
                return 40;
            
                break;
        
    




-(UIView*)tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section 
    if (section == kUserAchievementSection) 
        SegmentControlHeader *sectionHeaderView = [self.mTableView dequeueReusableHeaderFooterViewWithIdentifier:SegmentHeaderViewIdentifier];
        [sectionHeaderView setDelegate:self];
        [sectionHeaderView.segmentControl setSelectedSegmentIndex:self.userAchievementsType];
        return sectionHeaderView;
    
    return nil;

-(UITableViewCell*)tableView:(UITableView*)aTableView cellForRowAtIndexPath:(NSIndexPath*)indexPath 
    switch (indexPath.section) 
        case kUserCoverSection:
        
            static NSString* bannerCellType = @"kCoverBannerCellIdentifier";
            CoverBannerCell *cell = (CoverBannerCell*)[aTableView dequeueReusableCellWithIdentifier:bannerCellType];

            cell.tag = indexPath.row;
            if (cell == nil) 
                NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CoverBannerCell" owner:nil options:nil];
                cell = (CoverBannerCell*)[nib objectAtIndex:0];
            

            return cell;
        
        break;

        case kUserAchievementSection:
        
            switch (self.userAchievementsType) 
                case kUserGoldsCollections:
                 
                    static NSString* cellType = @"kUserGoldsViewCellIdentifier";
                    UserGoldsViewCell *cell = (UserGoldsViewCell*)[aTableView dequeueReusableCellWithIdentifier:cellType];
                    cell.tag = indexPath.row;
                    if (cell == nil) 
                        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"UserGoldsViewCell" owner:nil options:nil];
                        cell = (UserGoldsViewCell*)[nib objectAtIndex:0];
                    

                    return cell;
                
                    break;
                case kUserBronzeCollections:
                case kUserSilverCollections:
                    static NSString* cellType = @"kUserSilverCellIdentifier";
                    UserSilverCell *cell = (UserSilverCell*)[aTableView dequeueReusableCellWithIdentifier:cellType];
                    cell.tag = indexPath.row;
                    if (cell == nil) 
                        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"UserSilverCell" owner:nil options:nil];
                        cell = (UserSilverCell*)[nib objectAtIndex:0];
                    
                    return cell;
                
                break;

                default:  
                    return [self defaultCell];
                
                break;
            

        
        break;

        default:
            return nil;
            break;
    



-(void)didChangeSegmentValue:(UISegmentedControl *)segment
    NSInteger index = segment.selectedSegmentIndex;
    NSInteger lastSelectedIndex = self.userAchievementsType;
    switch (lastSelectedIndex) 
        case kUserGoldsCollections: offsetsArray [kUserGoldsCollections] = @(mTableView.contentOffset.y);
            break;
        case kUserSilverCollections: offsetsArray [kUserSilverCollections] = @(mTableView.contentOffset.y);
            break;
        case kUserBronzeCollections: offsetsArray [kUserBronzeCollections] = @(mTableView.contentOffset.y);
            break;
        default:
            break;
    
    //check if last table scroll header hit top
    if ([offsetsArray[lastSelectedIndex] floatValue] > 144) 
        for (int i = 0; i < [offsetsArray count]; i++) 
            if ([offsetsArray[i] floatValue] < 145 && offsetsArray[lastSelectedIndex] != offsetsArray[i]) 
                offsetsArray[i] = @(145);
            
        
    else
        for (int i = 0; i < [offsetsArray count]; i++) 
            if ([offsetsArray [i] floatValue] <= 145 && offsetsArray[lastSelectedIndex]!=offsetsArray[i]) 
                CGFloat newOffset;
                newOffset = [offsetsArray[i] floatValue] + ([offsetsArray[lastSelectedIndex] floatValue] -[offsetsArray[i] floatValue] );
                offsetsArray [i] = @(newOffset);
            else
                CGFloat newOffset = [offsetsArray[lastSelectedIndex] floatValue];
                offsetsArray [i] = @(newOffset);
            

        
    
    NSRange range = NSMakeRange(1, 1);
    NSIndexSet *section = [NSIndexSet indexSetWithIndexesInRange:range];
    self.userAchievementsType = index;
    [self.mTableView beginUpdates];
    [self.mTableView reloadSections:section withRowAnimation:UITableViewRowAnimationAutomatic];
    [self.mTableView endUpdates];
    [mTableView setContentOffset:CGPointMake(0, [offsetsArray[self.userAchievementsType] floatValue])];


我的TableView 正确显示,数据正确,滚动位置正确。 您会在我的代码中注意到 kUserSilverCollectionskUserBronzeCollections 显示相同的自定义单元格,并且具有相同的高度,但加载不同的数据源。当用户选择 kUserGoldsCollections(kUserGoldsCollections 的高度大于输入kUserSilverCollections & kUserBronzeCollections) ,然后返回输入kUserSilverCollectionskUserBronzeCollections,自定义单元格不再响应触摸,单元格内的按钮和单元格本身不识别触摸,还有一种情况最后几个细胞正在响应。但其余的都不是。

我在 ios 7.1 SDK 中编写代码。请帮助我找出导致此问题的原因:) 谢谢!

编辑

我发现了导致问题的原因,这是我的UserGoldsViewCell 现在我觉得很愚蠢:D 我的问题是缺少像我的单元设置这样的细节。我应该在这个问题中包含我的单元格代码。我将发布解决方案/详细信息作为未来读者的答案。

【问题讨论】:

如果您在现有代码方面需要帮助,那么您需要发布实际代码。没有人可以通过查看虚构的伪代码来告诉您代码有什么问题。 @rmaddy 谢谢你的建议。我编辑了问题并发布了我目前拥有的代码.. 您是想将表格视图定位到相同的像素内容偏移量还是相同的逻辑(就单元格数而言)偏移量? 我猜它搞砸了,因为你在动画完成之前调整了contentOffset。这种方法似乎不必要地复杂——为什么不只拥有 3 个不同的 tableviews,并使用分段控件来切换哪个是可见的? (他们仍然可以拥有相同的 delegate 对象。) @AaronBrager 同样的问题,即使我没有实现滚动 (setContentOffset) 功能。我需要我的数据在一个 UITableView :) 【参考方案1】:

我不知道你为什么在 Nib 中使用这种方法:

NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"UserSilverCell" owner:nil options:nil];
cell = (UserSilverCell*)[nib objectAtIndex:0];

使用这个,可能是你的问题:

在您的viewDidLoad 中,只需为您的 nib 注册一个 cellIdenfier:

[_yourTableView registerNib:[UINib nibWithNibName:@"UserSilverCell" 
                     bundle:[NSBundle mainBundle]] 
     forCellReuseIdentifier:@"kUserSilverCellIdentifier"];

然后在你的cellForRowAtIndexPath:

UserSilverCell *cell = (UserSilverCell *)[tableView     
            dequeueReusableCellWithIdentifier:@"kUserSilverCellIdentifier" 
                                 forIndexPath:indexPath];

您还必须将此应用于您的其他类型的单元格。

这可能是问题所在,但无论如何保持这种方式因为Nib更好。

【讨论】:

很确定这不是问题,但使用现代方法构建单元是一个很好的建议。 OP 使用的是 pre-os6 的方法,但这不是问题所在。 这是我创建单元格的原始方法。但产生同样的问题:) 感谢您的建议。 你还有其他问题..不是这段代码的问题.. P.S.有些人不理解投反对票的意义……除非答案可以改进项目或代码,否则不应该投反对票。【参考方案2】:

我找到了导致此问题的原因。是我的UserGoldsViewCell 覆盖了hitTest:withEvent 方法。以下是我对该方法的旧实现:

- (UIView *)hitTest:(CGPoint) point withEvent:(UIEvent *)event 
    if ([self pointInside:point withEvent:event]) 
        return scrollView;
    

    return nil;

我重写了这个方法,因为我的UserGoldsViewCell 中有一个UISrollView,而对于reason,我需要重写它。我的UserSilverCell 在选择UserGoldsViewCell 后没有响应触摸事件的原因是因为即使呈现的单元格是UserSilverCell 实例,我的UserGoldsViewCell 中的hitTest:withEvent 仍然被调用并返回其'scrollView。我改变了这个方法的实现,如下所示:

-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event

    UIView* childObj = nil;
    if ((childObj = [super hitTest:point withEvent:event]) == self)
        return self.scrollView;     
    return childObj;

【讨论】:

以上是关于UITableView 在运行时切换到不同的 CustomCells的主要内容,如果未能解决你的问题,请参考以下文章

在选项卡之间切换时 UITableView 不更新

UITableView 在点击屏幕之前不可见

UITableView 切换视图时变慢

加载视图后如何在运行时更新 UITableView

UITableView 重新加载时闪烁

切换到 UITableView 控制器并滚动到部分的开头