【问题标题】:Adding unknown number of rows to 'Static Cells' UITableView将未知数量的行添加到“静态单元”UITableView
【发布时间】:2012-04-20 01:46:05
【问题描述】:

我在 Interface Builder 中创建了一个静态表,其中包含 6 个部分,所有部分的行数都不同。 我现在想添加具有不同行数的第 7 部分。

首先,一旦我取消注释 Xcode 插入的标准表委托方法,我就会在 self.tableView.tableHeaderView = containerView;我在表格中添加了标题。

更重要的是,下面的代码让我崩溃了

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

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (section==6) {
        return 4;
    } else {
        return [super tableView:tableView numberOfRowsInSection:section];
    }
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{/*
    if (indexPath.section == 6) {
        static NSString *CellIdentifier = @"cellWireless";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

        // Configure the cell...

        return cell;
    }*/
    return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}

我如何正确地保留现有部分,但添加一个带有几个单元格的额外部分?

【问题讨论】:

  • 您必须检查每个部分的行数。否则您应该将更新的数据模型发送到您的 tableview 以使用更新的数据重新加载数据。
  • 确实返回 [super tableView:tableView numberOfRowsInSection:section];不返回每个部分的行数?
  • 上述方法是当您使用原型单元格时(dequeueReusableCellWithIdentifier)。您正在使用静态单元格。
  • "...根据单元格是用于静态行内容还是动态行内容,采用两种方法之一。对于动态内容,表格视图是一个列表,其中包含大量(并且可能是无限的)行。对于静态内容,行数是有限的、已知的数量..." 来源:developer.apple.com/library/ios/#documentation/UserExperience/…
  • 我在上一篇文章中的devforums.apple.com/message/502990#502990 找到了解决方案。您必须覆盖每个 tableview 委托方法。

标签: objective-c xcode cocoa-touch uitableview


【解决方案1】:

要将动态单元格添加到静态单元格表​​中,您必须覆盖每个具有 indexPath 的 UITableView 委托方法。

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath

-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath

-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
-(NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath

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

.

-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
     return NO;
}

-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{     
     return NO;
}

-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{     
     return UITableViewCellEditingStyleNone;     
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
     int section = indexPath.section;

     // if dynamic section make all rows the same height as row 0
     if (section == self.dynamicSection) {
          return [super tableView:tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]];
     } else {
          return [super tableView:tableView heightForRowAtIndexPath:indexPath];
     }
}

- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
{
     int section = indexPath.section;

     // if dynamic section make all rows the same indentation level as row 0
     if (section == self.dynamicSection) {
          return [super tableView:tableView indentationLevelForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]];
     } else {
          return [super tableView:tableView indentationLevelForRowAtIndexPath:indexPath];
     }
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
     if (section == self.dynamicSection ) {
          return [self.dataListArray count];
     } else {
          return [super tableView:tableView numberOfRowsInSection:section];
     }
}

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


     if (section == self.dynamicSection) {
          // make dynamic row's cell
          static NSString *CellIdentifier = @"Dynamic Cell";
          UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

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

          cell.textLabel.text = [self.dataListArray objectAtIndex:row];
          return cell;
    } else {
          return [super tableView:tableView cellForRowAtIndexPath:indexPath];
    }
}

只有在您覆盖所有方法后,您的表才会开始工作。对于任何引用静态部分的内容,只需参考 [super]。

【讨论】:

  • 实际上,如果你想动态创建尽可能多的部分,你也需要实现numberOfSectionsInTableView:。为了解决超出数组范围的问题,您需要使用 indexPath 覆盖所有方法!有些未在此处列出,例如 titleForHeaderInSection 或 heightForHeaderInSection 和 viewForHeaderInSection。
  • 我认为您不需要像this answer建议的那样覆盖静态表的所有方法以使其成功。我将提供一个单独的答案,说明什么对我有用。不需要覆盖所有方法,但您需要覆盖的内容提供一个超级实现以及您的自定义实现。
  • 这对我有用,但只是部分。该表不显示自定义单元格,而是显示一个空单元格(具有正确的大小等)。根据我的经验,我可以看出dequeueReusableCellWithIdentifier 总是在静态表中返回nil,因此我们只剩下cellForRowAtIndexPath,这无济于事,因为我们想向该部分添加更多单元格,而不是改变我们已经拥有的那些。所以我们最终使用MyCustomCell *cell = [super.tableView dequeueReusableCellWithIdentifier:@"MyCustomCellId"]; 手动创建新单元格,这似乎也不起作用。有什么线索吗?
  • This question 似乎与我之前的评论有关。
  • jweyrich,我也有同样的问题。你有什么进展吗?这是我的问题:stackoverflow.com/questions/12858363/…
【解决方案2】:

Darren 的回答让我知道什么对我有用,但是我不必去实现每一个 tableView 委托方法。你真的只需要覆盖 numberOfRowsInSection 和 cellForRowAtIndexPath。

首先,我在 Interface Builder 中定义了一个静态表,其中包含 4 个部分,每个部分包含 2 到 4 个单元格。我希望第 0、2 和 3 部分是静态的,并且看起来与 IB 中的完全一样,但我希望第 1 部分具有自定义行数,并根据我拥有的值数组在每个单元格中自定义显示。

在静态表的视图控制器中,覆盖为您的动态部分返回的单元格数,但接受所有其他部分的默认值(它们将回退到 IB 值)。对 cellForRowAtIndexPath 执行相同操作,并为除第 1 节之外的所有节返回 [super] 实现。

@implementation myMostlyStaticTableViewController
@synthesize myFancyArray;

- (NSInteger) tableView:(UITableView *) tableView numberOfRowsInSection:(NSInteger) section
{
    if (section == 1)
        return [myFancyArray count]; // the number of rows in section 1
    else
        return [super tableView:tableView numberOfRowsInSection:section];
}

- (UITableViewCell *) tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath *) indexPath
{
    // for cells not in section 1, rely on the IB definition of the cell
    if (indexPath.section != 1)
        return [super tableView:tableView cellForRowAtIndexPath:indexPath];

    // configure a task status cell for section 1
    MyCustomTableViewCell *cell;
    cell = [tableView dequeueReusableCellWithIdentifier:@"myCustomCell"];
    if (!cell)
    {
        // create a cell
        cell = [[MyCustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"myCustomCell"];
    }
    cell.myCustomLabel.text = [myFancyArray objectAtIndex:indexPath.row];
    return cell;
}
@end

当然你需要一个自定义单元格:

@implementation MyCustomTableViewCell

- (UITableViewCell *) initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    // initialize cell and add observers
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (!self)
        return self;
    self.clipsToBounds = YES;
    self.selectionStyle = UITableViewCellSelectionStyleNone;

    // configure up some interesting display properties inside the cell
    _label = [[UILabel alloc] initWithFrame:CGRectMake(20, 9, 147, 26)];
    _label.font = [UIFont fontWithName:@"HelveticaNeue-Medium" size:17];
    _label.textColor = [UIColor colorWithWhite:0.2 alpha:1];
    [self.contentView addSubview:_label];

    return self;
}

@end

【讨论】:

    【解决方案3】:

    我将在 Swift 中发布答案,但它也应该可以在 Objective-C 中使用。

    根据我的经验,覆盖UITableViewController 中的这些方法就足够了:

    tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
    tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
    tableView(tableView: UITableView, indentationLevelForRowAtIndexPath indexPath: NSIndexPath) -> Int
    

    如果你想在你的表格视图中有自定义表格视图单元格,你需要用 nib 来创建 UITableViewCell 的子类,并将它注册到你的表格视图中。

    我的整个控制器如下所示:

    var data = ["Ahoj", "Hola", "Hello"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        tableView.registerNib(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: "reuseIdentifier")
    }
    
    // MARK: - Table view data source
    
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if section == 1 {
            return data.count
        }
        return super.tableView(tableView, numberOfRowsInSection: section)
    }
    
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        if indexPath.section == 1 {
            let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath) as! CustomCell
            cell.titleLabel.text = data[indexPath.row]
            return cell
        }
        return super.tableView(tableView, cellForRowAtIndexPath: indexPath)
    }
    
    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return 44
    }
    
    override func tableView(tableView: UITableView, indentationLevelForRowAtIndexPath indexPath: NSIndexPath) -> Int {
        return 0
    }
    
    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
        if indexPath.section == 1 {
            print(data[indexPath.row])
        }
    }
    
    @IBAction func addItem() {
        data.append("Item \(data.count)")
        tableView.beginUpdates()
        tableView.insertRowsAtIndexPaths([NSIndexPath(forRow: data.count - 1, inSection: 1)], withRowAnimation: .Left)
        tableView.endUpdates()
    }
    
    @IBAction func removeItem() {
        if data.count > 0 {
            data.removeLast()
            tableView.beginUpdates()
            tableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: data.count, inSection: 1)], withRowAnimation: .Left)
            tableView.endUpdates()
        }
    }
    

    【讨论】:

    • “CustomCell”和“ReuseIdentifier”从何而来?
    • 这种方法对我有用。在我覆盖 indentationLevelForRowAtIndexPath 之前,我遇到了错误。
    • 它崩溃了。如果第 1 节的数据超过 1 行。怎么了?
    • 它崩溃了,但我忘了实现 heightForRowAt 然后添加它后,它工作了,谢谢。
    【解决方案4】:

    我想我会根据@Darren 的出色答案添加一个更新的答案。大多数委托方法不是必需的。所以,我只是添加了所需的。如果您愿意,您可以轻松添加自定义单元格,甚至使用 nib 文件。该图显示了一个包含 3 个部分的静态表。最后一部分是运行时动态的。这非常方便。顺便说一句,这在 ios7 中有效。

    #define DYNAMIC_SECTION 2
    
    #import "MyTableViewController.h"
    
    @interface MyTableViewController ()
    @property (strong, nonatomic)NSArray *myArray;
    @end
    
    @implementation MyTableViewController
    
    - (id)initWithCoder:(NSCoder *)aDecoder
        {
            if (self = [super initWithCoder:aDecoder]) {
                _myArray = @[@"ONE", @"TWO", @"THREE", @"FOUR"];
            }
            return self;
        }
    
        - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
        {
            return [super numberOfSectionsInTableView:tableView];
        }
    
        - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
        {
            if (section != DYNAMIC_SECTION) {
                return [super tableView:tableView numberOfRowsInSection:section];
            }
            return [self.myArray count];
        }
    
        - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
        {
            if (indexPath.section != DYNAMIC_SECTION) {
                return [super tableView:tableView cellForRowAtIndexPath:indexPath];
            }
            static NSString *id = @"MyCell";
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:id];
            if (!cell) {
                cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:id];
            }
            cell.textLabel.text = self.myArray[indexPath.row];
            return cell;
        }
    
            // required
        -(NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
        {
            int section = indexPath.section;
            if (section == DYNAMIC_SECTION) {
                return [super tableView:tableView indentationLevelForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]];
            } else {
                return [super tableView:tableView indentationLevelForRowAtIndexPath:indexPath];
            }
        }
    
                // Not required
        - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
        {
            if (section != DYNAMIC_SECTION) {
                return [super tableView:tableView titleForHeaderInSection:section];
            }
            return @"some title";
        }
    

    【讨论】:

    • 感谢您的回答。值得注意的是,如果您使用的是情节提要,则此答案要求表格视图的第 2 部分应包含至少一个可以从中获取单元格高度的单元格。否则它会因为数组越界而崩溃......!
    • 我还发现需要覆盖 tableView:heightForRowAtIndexPath: 委托方法(至少在 iOS 9 下!)@smileBot: // 需要 -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:( NSIndexPath *)indexPath { NSInteger section = indexPath.section; if (section == DYNAMIC_SECTION) { return [super tableView:tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]]; } else { return [super tableView:tableView heightForRowAtIndexPath:indexPath]; } }
    • 我创建了一个项目来演示这个工作:github.com/fatuhoku/…
    【解决方案5】:

    我认为您将不得不使您的 UITableView 动态化。由于您有“未知”的行数,您很可能会将委托方法设置为:

     - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return [someArray count];
    }
    

    【讨论】:

    • 谢谢。我已经在 cmets 中说我已经整理好了。
    【解决方案6】:

    我发现了一些我认为非常有趣的东西,它比“评论”更值得回答。我有这个动态行工作的静态 tableView,然后它停止工作。原因很简单。我以前有

    [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]

    后来决定我想要/需要一个自定义单元格,我将在我的 StoryBoard 中设计它,并且只为我的 UITableView 子类设置插座。所以我使用了其他技术

    [super tableView:tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:indexPath.section]];

    这里的问题似乎是该单元格被重复使用,因此您一次只能看到一个单元格。有时你甚至什么都看不到,它们都是空的!如果您滚动,您会看到其他单元格很快出现然后消失(更像是闪烁!)。

    这让我非常抓狂,直到我意识到什么是(不可能)。此外,不要尝试这样做

    [super.tableView dequeueReusableCellWithIdentifier:CellIdentifier]

    因为正如其他人所提到的,这总是在静态 tableView 中返回nil。

    ——

    所以我不确定该怎么做。我想我会使用“静态原型”路线,其中包括

    • 对第 3 行第 1 行使用带有单元格标识符(如“31”)的原型表视图。然后我可以执行类似的操作
    NSString *identifier = [NSString stringWithFormat:@"%d%d", indexPath.section, indexPath.row];
    cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    • 对标题也使用原型单元。我使用"Cell1-Header" 作为第 1 节标题的单元格标识符,然后有类似
    - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
    {
        NSString *identifier = [NSString stringWithFormat:@"Cell%d-Header", section];
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
        返回单元格内容视图;
    }

    这里的基本内容是你总是可以从一个静态 tableView 开始,但是当你意识到你需要一些动态的东西时,把它换成 Prototype(它会保留你的行,虽然我不记得它对这些部分做了什么!)并使用这种 KISS 技术。

    【讨论】:

      【解决方案7】:

      我想我找到了一个更好、更简单的解决方案,在 IB 中使用“fantom”部分或行。

      如果您知道将在第 7 节中使用的最大单元格数(假设为 10),则应在 IB 中配置第 7 节时将行数设置为 10。

      您不必强制使用部分中的所有 10 行,这可以通过设置

       -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section.
      

      例如,如果在 section == 6(实际上是第 7 节)时返回 5,则只会显示 5 行。

      我承认这不是绝对意义上的动态,但也许可以解决大多数情况。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-07-08
        • 1970-01-01
        • 2020-06-03
        • 2014-01-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多