【问题标题】:How to remove a Static Cell from a UITableView designed in StoryBoard如何从 StoryBoard 中设计的 UITableView 中删除静态单元格
【发布时间】:2012-01-05 22:19:45
【问题描述】:

解决方案可能很简单,但我找不到它..!

使用故事板 (iOS 5),我有一个 tableViewController,以及一个设计有 5 个部分的静态表格视图,每个部分内都有不同的静态单元格。

我的问题是:如何以编程方式删除viewWillAppear 中的单元格?

例如,我有一个为日期设计的单元格

IBOutlet UITableViewCell * cellForDate;

而且.. 如果没有日期,我想删除我的单元格。

cellForDate.hidden = true; //Hide the cell, but leave a blank space

我试过[tableView deleteRowsAtIndexPaths...] 没用

有人有想法吗?

【问题讨论】:

    标签: iphone uitableview storyboard


    【解决方案1】:

    UITableViewDelegatetableView:willDisplayCell:forRowAtIndexPath: 方法中尝试在显示之前隐藏单元格。这是您可以操纵单元格外观的最后一种方法。但是,这不会删除单元格应占用的空间,因此您可以尝试的另一件事是使用同一协议的 tableView:heightForRowAtIndexPath: 方法将单元格行的高度设置为 0。

    另一种最稳健的方法是设计一种方法来确定是否需要节中的日期单元格,并根据结果返回节中正确的行数,并返回其他节的行单元格情况考虑。然后在 viewWillAppear 上重新加载 tableView 的数据。

    【讨论】:

    • 实际上,这是我发现解决问题的唯一方法,使用方法 heightForRowAtIndexPath,有一个 IndexPath 开关并在此函数内进行验证。如果它为零,我将使用 IBOutlet UITableViewCell 引用隐藏单元格,并返回 0.. 单元格消失了!当有超过 20 个单元时,它变得有点令人毛骨悚然,但效果很好!
    【解决方案2】:

    希望这是一个更通用的解决方案,但它仍然不完美

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
        if (cell.hidden) {
            return 0;
        } else {
            return [super tableView:tableView heightForRowAtIndexPath:indexPath];
        }
    }
    

    使用此方法,您将 IBOutlet 绑定单元格设置为隐藏,然后当表格尝试显示一行时,它首先检查是否应该隐藏单元格(使用超级调用获取单元格),如果是的话它返回高度 0,有效地将其从列表中删除。如果不应该隐藏单元格,那么它会从超级单元格中获取高度,因此通常会发生的事情仍然会发生。

    唯一的问题是开始和结束单元格负责列表或组顶部和底部的分隔符。因此,如果您隐藏组中的最后一个单元格,则该组的底部分隔线将略微插入,就像在正常行上而不是全宽一样。仅当您隐藏顶行或底行并且使用分隔线并且您关心完全标准的显示时,这才是问题。在我的情况下,最好的解决方案就是不隐藏顶行或底行。我将任何可能隐藏的内容移到了组的中间。或者,您可以接受它不是很标准,或者禁用表格上的行分隔符。

    【讨论】:

    • 正如您所说,这并不完美,因为它会产生递归溢出,但是如果您知道要隐藏的行及其状态,则可以跳过 cellForRowAtIndexPath 调用,它可以完美运行.
    • @Hola Soy Edu Feliz Navidad 我没有看到任何递归。已测试 iOS 9。
    • 只有在你的 cellForRowAtIndexPath 以某种方式调用 heightForRowAtIndexPath 时才会创建递归。这并非不合理,但也不是标准行为。如果您遵循最佳布局做法,则不需要两个位置的高度。
    • 关于部分分隔符的良好描述 :) 我标记了它并使用它。
    【解决方案3】:

    斯威夫特 3

    override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        if indexPath.section == 0 {
            cell.isHidden = true
        }
    }
    
    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        if indexPath.section == 0 {
            return 0
        } else {
            return super.tableView(tableView, heightForRowAt: indexPath)
        }
    }
    

    【讨论】:

    • 也适用于 Swift 4
    【解决方案4】:

    如果您想永久删除 tableview 单元格,只需调用 deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone] 并使用与您的行对应的 indexPath。您还需要减少numberOfRowsInSection: 返回的行数。

    但是,我一直在寻找一种方法来从静态表格视图中临时“删除”不需要的静态表格视图单元格,因为我希望某个特定的行有时存在,而其他时候则不存在。我还希望能够从 tableview 控制器外部按需更新。

    我的情况相当简单,因为它是显示或隐藏的第一行。您可以根据自己的需要进行概括。我的 tableview 控制器是我的数据源委托。

    首先,我在tableview控制器中创建了一个公共方法来更新状态变量并触发重新显示:

    - (void)updateSettingsDisplay:(BOOL)hideSetting
    {
        if (hideSetting == self.hideSetting) {
            return; // no need to do anything because it didn't change
        }
    
        self.hideSetting = hideSetting;
        self.numRows = (hideSetting)? kNumRowsWithSetting : kNumRowsWithoutSetting;
    
        // redisplay only if we're visible
        if (!self.viewJustLoaded && (self.navController.visibleViewController == self)) {
            [self.tableView reloadData];
        }
        self.viewJustLoaded = NO;
    }
    

    tableview 控制器的viewDidLoad 看起来像:

    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        // check whether or not to display out-of-coverage tableview cell
        self.hideSetting = YES; // initial value; could just as easily be NO
        self.viewJustLoaded = YES;
        [self updateSettingsDisplay];
    }
    

    tableview 的数据源委托:

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        // Return the number of rows in the section.
        return self.numRows;
    }
    

    最后

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        // set row to the index of the stored tableView cell that corresponds to the
        // cell you want (its location in the static tableview from your storyboard)
        NSInteger row = indexPath.row;
        if (self.hideSetting) {
            // skip drawing setting's tableviewcell (since the setting we're hiding is
            // first, we can just increment the row to get the one we want)
            ++row;
            assert(row < kTotalRows); // bounds checking just to convince yourself (remove after testing)
        }
        // get a new index path since the row field is read-only
        NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:row inSection:indexPath.section];
    
        // grab the cell from super that corresponds to the cell you want
        UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:newIndexPath]; // static table
    
        return cell;
    }
    

    诀窍是静态单元格在[super tableView:tableView cellForRowAtIndexPath:newIndexPath] 中始终可用 - 所以使用它来永久存储静态表格视图单元格。然后根据需要调整行数,并在您的 tableview 委托的cellForRowAtIndexPath: 中正确映射行(即,获取与您要显示的单元格对应的存储单元格的行)。

    updateSettingsDisplay 方法可以由任何保留它的类在您的 tableview 控制器上调用。如果调用时tableview控制器是不可见的,它只会更新状态,等到下次可见再改变显示。

    【讨论】:

    • 你答案的第二部分看起来不错,但第一段deleteRowsAtIndexPaths没有效果
    【解决方案5】:
    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    
        if(indexPath.section == 1) { //For example
            return 0
        }
    }
    
    override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
        if(indexPath.section == 1) {
             cell.hidden = true
        }
    }
    

    【讨论】:

    • heightForRowAtIndexPath方法中else部分如何返回高度
    【解决方案6】:

    你可能想看看这个

    https://github.com/xelvenone/StaticDataTableViewController/

    用法

    self.hideSectionsWithHiddenRows = YES; //YES, NO
    [self cell:self.outletToMyStaticCell1 setHidden:hide];
    [self cell:self.outletToMyStaticCell2 setHidden:hide];
    [self reloadDataAnimated:YES];
    

    【讨论】:

      【解决方案7】:

      仅供参考(如果可以的话,我会发表评论),如果您要覆盖 Storyboard 中的 Grouped static UITableView,那么您还需要覆盖您正在修改/隐藏的每个部分的页眉和页脚视图和高度。

      我还需要将“GetHeight”方法的返回值设置为零以外的值 - 这意味着零值意味着其他值(自动布局或“未设置”等)。

      以下代码在 Xamarin iOS C# 中,但直接转换为适当的 Obj-C 方法:

          public override void WillDisplayHeaderView(UITableView tableView, UIView headerView, nint section)
          {
              if (section == 0)
              {
                  headerView = new UIView(new CGRect(0,0,0,0));
              }
          }
      
          public override nfloat GetHeightForHeader(UITableView tableView, nint section)
          {
              if (section == 0)
              {
                  return 1f;
              }
              return base.GetHeightForHeader(tableView, section);
          }
      
          public override void WillDisplayFooterView(UITableView tableView, UIView footerView, nint section)
          {
              if (section == 0)
              {
                  footerView = new UIView(new CGRect(0, 0, 0, 0));
              }
          }
      
          public override nfloat GetHeightForFooter(UITableView tableView, nint section)
          {
              if (section == 0)
              {
                  return 1f;
              }
              return base.GetHeightForFooter(tableView, section);
          }
      

      【讨论】:

        【解决方案8】:

        您需要将beginUpdates 放在删除调用之前和endUpdates 之后:

        [tableView beginUpdates];
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:r inSection:s]]];
        [tableView endUpdates];
        

        回应您的评论:

        - (NSInteger)tableView:(UITableView)tableView numberOfRowsInSection:(NSInteger)section {
            NSInteger ret;
            switch (section) {
                case 0:
                    // sectionZeroRows doesnt have to be a 
                    // property (an attribute works just fine), but it
                    // helps with explaining this example.
                    ret = self.sectionZeroRows;
                    break;
                // ...
            }
            return ret;
        }
        
        - (void)someMethod {
            --self.sectionZeroRows;
            [tableView beginUpdates];
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]]];
            [tableView endUpdates];
        }
        

        【讨论】:

        • 它给了我崩溃错误:*** 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“无效更新:第 4 节中的行数无效。现有文件中包含的行数更新后的节 (3) 必须等于更新前该节中包含的行数 (3),加上或减去从该节插入或删除的行数(0 插入,1 删除)加或减移入或移出该部分的行数(0移入,0移出)。'
        • 哦,是的,您需要设置该部分的单元格数.. 在您的 tableView:numberOfRowsInSection: 方法中,无论变量保持行数,将其减 1,因此此方法将返回 4该部分的 5 个(或者需要多少个)。
        • 好吧,我明白了,我正在尝试一种新的方式来使用 iOS 5 中的 TableViewController 来处理情节提要和表格视图,并且我没有覆盖委托和数据源方法。因为我的数据源是我的静态表包含在我的故事板中.. /////// 你如何使用 numberOfRowsInSection 来知道我是否刚刚删除了一行,因为我正在从 viewDidAppear 方法中删除该行。我应该为每个部分保留一个 var,然后更新我的 var 吗?
        • 原因:'无效的表格视图更新。应用程序请求更新与数据源提供的状态不一致的表视图。'......这让我发疯了!哈哈
        • 在此之间,感谢您为我提供的巨大帮助。我完全喜欢你的例子。仍然有这个错误:***由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“无效的表视图更新。应用程序已请求更新与数据源提供的状态不一致的表视图。 *** ////// 但是如果注释 deleterows 行,它可以工作! ...这很奇怪,看起来它不会删除静态的东西
        【解决方案9】:
        // just authed with facebook, and I want to delete the first 3 rows of my 4 row table that are no longer necessary    
        - (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
            if(wantsFacebook)
                return 1;
        
            return 4;
        }
        

        提交更新后,tableView 将调用 numberOfRowsInSection 委托来获取更新的行数,并且它必须是您开始插入/删除时的行与结束插入/时的行之间的正确总和/差值删除

        【讨论】:

          猜你喜欢
          • 2012-05-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-05-24
          • 1970-01-01
          • 1970-01-01
          • 2017-06-17
          • 1970-01-01
          相关资源
          最近更新 更多