UITableView设置为静态单元格.是否有可能以编程方式隐藏一些单元格?

Shm*_*idt 128 objective-c uitableview ios

UITableView 设置为静态单元格.

是否有可能以编程方式隐藏一些单元格?

Jus*_*tas 163

隐藏UITable中的静态单元格:

  1. 添加此方法:

在您的UITableView控制器委托类中:

Objective-C的:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell* cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];

    if(cell == self.cellYouWantToHide)
        return 0; //set the hidden cell's height to 0

    return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
Run Code Online (Sandbox Code Playgroud)

迅速:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    var cell = super.tableView(tableView, cellForRowAtIndexPath: indexPath)

    if cell == self.cellYouWantToHide {
        return 0
    }

    return super.tableView(tableView, heightForRowAtIndexPath: indexPath)
}
Run Code Online (Sandbox Code Playgroud)

将为UITable中的每个单元调用此方法.一旦它为你想要隐藏的单元格调用它,我们将其高度设置为0.我们通过为它创建一个出口来识别目标单元格:

  1. 在设计器中,为要隐藏的单元格创建一个插座.一个这样的细胞的出口在上面被称为"cellYouWantToHide".
  2. 在IB中选中要隐藏的单元格的"剪辑子视图".您隐藏的单元格需要具有ClipToBounds = YES.否则文本将堆积在UITableView中.

  • ClipToBounds = YES的+1.其他线程中的Numeorus解决方案已经错过了. (13认同)
  • 好的解决方案 要避免出现`ClipToBounds`问题,您还可以将单元格设置为隐藏.这对我来说似乎更清洁.;-) (10认同)
  • 很好的主意.这是我发现的最简单的解决方案.其他解决方案在两个或更多位置具有代码.顺便说一句,在IB中,ClipToBounds被列为"剪辑子视图". (2认同)
  • 在不创建单元格对象的情况下,我们也可以使用“ index.row”隐藏“ uitableviewcell” (2认同)

Pet*_*isu 48

您正在寻找此解决方案:

StaticDataTableViewController 2.0

https://github.com/xelvenone/StaticDataTableViewController

它可以显示/隐藏/重新加载任何静态单元格,无论是否有动画!

[self cell:self.outletToMyStaticCell1 setHidden:hide]; 
[self cell:self.outletToMyStaticCell2 setHidden:hide]; 
[self reloadDataAnimated:YES];
Run Code Online (Sandbox Code Playgroud)

注意总是只使用(reloadDataAnimated:YES/NO)(不要直接调用[self.tableView reloadData])

这不会使用设置高度为0的hacky解决方案,并允许您设置更改动画并隐藏整个部分

  • 该解决方案的一个问题是它留下了隐藏细胞所在的空白. (5认同)

Moh*_*leh 37

最好的方法如以下博客中所述 http://ali-reynolds.com/2013/06/29/hide-cells-in-static-table-view/

在界面构建器中正常设计静态表视图 - 包含所有可能隐藏的单元格.但是你必须为你想要隐藏的每个潜在单元格做一件事 - 检查单元格的"剪辑子视图"属性,否则当你试图隐藏它时(通过缩小它的高度)单元格的内容不会消失 - 更晚些时候).

所以 - 你在一个单元格中有一个开关,开关应该隐藏并显示一些静态单元格.把它连接到IBAction并在那里执行此操作:

[self.tableView beginUpdates];
[self.tableView endUpdates];
Run Code Online (Sandbox Code Playgroud)

这为细胞出现和消失提供了很好的动画效果.现在实现以下表视图委托方法:

- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 1 && indexPath.row == 1) { // This is the cell to hide - change as you need
    // Show or hide cell
        if (self.mySwitch.on) {
            return 44; // Show the cell - adjust the height as you need
        } else {
            return 0; // Hide the cell
        }
   }
   return 44;
}
Run Code Online (Sandbox Code Playgroud)

就是这样.翻转开关,单元格隐藏,然后以漂亮,流畅的动画重新出现.

  • 这个确实有效,但要覆盖一个缺失点:如果要隐藏/显示的行包含日期选择器,只做[self.tableView beginUpdates]; [self.tableView endUpdates]; 隐藏它时仍然会留下一个小故障.要消除毛刺,你应该调用[self.tableView reloadRowsAtIndexPaths:@ [indexPathOfTargetRow] withRowAnimation:UITableViewRowAnimationNone]; 另外要注意行动画在某种程度上是"禁用"的. (2认同)

hen*_*g77 34

我的解决方案与Gareth的方向类似,尽管我做了一些不同的事情.

开始:

1.隐藏细胞

没有办法直接隐藏细胞.UITableViewController是提供静态单元格的数据源,目前没有办法告诉它"不提供单元格x".因此,我们必须提供自己的数据源,该数据源委托给它UITableViewController以获取静态单元格.

最简单的是子类化UITableViewController,并覆盖隐藏单元格时需要表现不同的所有方法.

在最简单的情况下(单节表,所有单元格具有相同的高度),这将是这样的:

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Recalculate indexPath based on hidden cells
    indexPath = [self offsetIndexPath:indexPath];

    return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}

- (NSIndexPath*)offsetIndexPath:(NSIndexPath*)indexPath
{
    int offsetSection = indexPath.section; // Also offset section if you intend to hide whole sections
    int numberOfCellsHiddenAbove = ... // Calculate how many cells are hidden above the given indexPath.row
    int offsetRow = indexPath.row + numberOfCellsHiddenAbove;

    return [NSIndexPath indexPathForRow:offsetRow inSection:offsetSection];
}
Run Code Online (Sandbox Code Playgroud)

如果您的表有多个部分,或者单元格具有不同的高度,则需要覆盖更多方法.同样的原则适用于此:您需要在委托给super之前偏移indexPath,section和row.

另请注意,类似方法的indexPath参数对于didSelectRowAtIndexPath:同一个单元格会有所不同,具体取决于状态(即隐藏的单元格数).因此,始终偏移任何indexPath参数并使用这些值可能是个好主意.

2.动画变化

正如Gareth已经说过的那样,如果使用reloadSections:withRowAnimation:方法设置动画更改,则会出现重大故障.

我发现如果你reloadData:之后立即打电话,动画会有很大的改进(只留下一些小故障).动画后表格会正确显示.

所以我要做的是:

- (void)changeState
{
     // Change state so cells are hidden/unhidden
     ...

    // Reload all sections
    NSIndexSet* reloadSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self numberOfSectionsInTableView:tableView])];

    [tableView reloadSections:reloadSet withRowAnimation:UITableViewRowAnimationAutomatic];
    [tableView reloadData];
}
Run Code Online (Sandbox Code Playgroud)

  • 祝福你,你甜蜜甜蜜的人. (2认同)

Sal*_*sum 13

  1. 在设计器中,为要隐藏的单元格创建一个插座.例如,您想隐藏'cellOne',因此在viewDidLoad()中执行此操作

cellOneOutlet.hidden = true

现在覆盖以下方法,检查哪个单元格状态是隐藏的,并返回这些单元格的高度0.这是您可以在swift中隐藏静态tableView中的任何单元格的众多方法之一.

override func tableView(tableView: UITableView, heightForRowAtIndexPathindexPath: NSIndexPath) -> CGFloat 
{

let tableViewCell = super.tableView(tableView,cellForRowAtIndexPath: indexPath)

        if tableViewCell.hidden == true
        {
            return 0
        }
        else{
             return super.tableView(tableView, heightForRowAtIndexPath: indexPath)
        }

}
Run Code Online (Sandbox Code Playgroud)


Aus*_*tin 11

我提出了一个实际隐藏部分的替代方案,但不删除它们.我尝试了@ henning77的方法,但是当我改变静态UITableView的部分数量时,我一直遇到问题.这种方法对我来说效果很好,但我主要是试图隐藏部分而不是行.我正在成功删除一些行,但它更麻烦,所以我试图将事物分组到我需要显示或隐藏的部分.这是我如何隐藏部分的示例:

首先,我声明一个NSMutableArray属性

@property (nonatomic, strong) NSMutableArray *hiddenSections;
Run Code Online (Sandbox Code Playgroud)

在viewDidLoad中(或在查询了数据之后),您可以将要隐藏的部分添加到数组中.

- (void)viewDidLoad
{
    hiddenSections = [NSMutableArray new];

    if(some piece of data is empty){
        // Add index of section that should be hidden
        [self.hiddenSections addObject:[NSNumber numberWithInt:1]];
    }

    ... add as many sections to the array as needed

    [self.tableView reloadData];
}
Run Code Online (Sandbox Code Playgroud)

然后实现以下TableView委托方法

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    if([self.hiddenSections containsObject:[NSNumber numberWithInt:section]]){
        return nil;
    }

    return [super tableView:tableView titleForHeaderInSection:section];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if([self.hiddenSections containsObject:[NSNumber numberWithInt:indexPath.section]]){
        return 0;
    }

    return [super tableView:tableView heightForRowAtIndexPath:[self offsetIndexPath:indexPath]];
}

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if([self.hiddenSections containsObject:[NSNumber numberWithInt:indexPath.section]]){
        [cell setHidden:YES];
    }
}
Run Code Online (Sandbox Code Playgroud)

然后将隐藏部分的页眉和页脚高度设置为1,因为您无法将高度设置为0.这会导致额外的2个像素空间,但我们可以通过调整下一个可见标题的高度来弥补它.

-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section 
{
    CGFloat height = [super tableView:tableView heightForHeaderInSection:section];

    if([self.hiddenSections containsObject:[NSNumber numberWithInt:section]]){
        height = 1; // Can't be zero
    }
    else if([self tableView:tableView titleForHeaderInSection:section] == nil){ // Only adjust if title is nil
        // Adjust height for previous hidden sections
        CGFloat adjust = 0;

        for(int i = (section - 1); i >= 0; i--){
            if([self.hiddenSections containsObject:[NSNumber numberWithInt:i]]){
                adjust = adjust + 2;
            }
            else {
                break;
            }
        }

        if(adjust > 0)
        {                
            if(height == -1){
                height = self.tableView.sectionHeaderHeight;
            }

            height = height - adjust;

            if(height < 1){
                height = 1;
            }
        }
    }

    return height;
}

-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section 
{   
    if([self.hiddenSections containsObject:[NSNumber numberWithInt:section]]){
        return 1;
    }
    return [super tableView:tableView heightForFooterInSection:section];
}
Run Code Online (Sandbox Code Playgroud)

然后,如果确实要隐藏特定行,则可以调整numberOfRowsInSection以及在cellForRowAtIndexPath中返回哪些行.在这个例子中,我有一个有三行的部分,其中任何三行都可以为空,需要删除.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSInteger rows = [super tableView:tableView numberOfRowsInSection:section];

    if(self.organization != nil){
        if(section == 5){ // Contact
            if([self.organization objectForKey:@"Phone"] == [NSNull null]){     
                rows--;
            }

            if([self.organization objectForKey:@"Email"] == [NSNull null]){     
                rows--;
            }

            if([self.organization objectForKey:@"City"] == [NSNull null]){     
                rows--;
            }
        }
    }

    return rows;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{    
    return [super tableView:tableView cellForRowAtIndexPath:[self offsetIndexPath:indexPath]];
}
Run Code Online (Sandbox Code Playgroud)

使用此offsetIndexPath计算有条件删除行的行的indexPath.如果你只是隐藏部分,则不需要

- (NSIndexPath *)offsetIndexPath:(NSIndexPath*)indexPath
{
    int row = indexPath.row;

    if(self.organization != nil){
        if(indexPath.section == 5){
            // Adjust row to return based on which rows before are hidden
            if(indexPath.row == 0 && [self.organization objectForKey:@"Phone"] == [NSNull null] && [self.organization objectForKey:@"Email"] != [NSNull null]){     
                row++;
            }
            else if(indexPath.row == 0 && [self.organization objectForKey:@"Phone"] == [NSNull null] && [self.organization objectForKey:@"Address"] != [NSNull null]){     
                row = row + 2;
            }
            else if(indexPath.row == 1 && [self.organization objectForKey:@"Phone"] != [NSNull null] && [self.organization objectForKey:@"Email"] == [NSNull null]){     
                row++;
            }
            else if(indexPath.row == 1 && [self.organization objectForKey:@"Phone"] == [NSNull null] && [self.organization objectForKey:@"Email"] != [NSNull null]){     
                row++;
            }
        }
    }

    NSIndexPath *offsetPath = [NSIndexPath indexPathForRow:row inSection:indexPath.section];

    return offsetPath;
}
Run Code Online (Sandbox Code Playgroud)

有很多方法可以覆盖,但我喜欢这种方法的方法是它可以重复使用.设置hiddenSections数组,添加它,它将隐藏正确的部分.隐藏行有点棘手,但可能.如果我们使用分组的UITableView,我们不能只将我们要隐藏的行的高度设置为0,因为边框将无法正确绘制.


Ser*_*kov 10

事实证明,您可以在静态UITableView中隐藏和显示单元格 - 以及动画.并不难实现.

演示项目

演示项目视频

要旨:

  1. Use tableView:heightForRowAtIndexPath: 根据某些状态动态指定单元格高度.
  2. 当状态通过调用更改显示/隐藏的动画单元格时 tableView.beginUpdates();tableView.endUpdates()
  3. 不要tableView.cellForRowAtIndexPath:在里面打电话tableView:heightForRowAtIndexPath:.使用缓存的indexPaths来区分单元格.
  4. 不要隐藏细胞.在Xcode中设置"剪辑子视图"属性.
  5. 使用自定义单元格(不是Plain等)来获得漂亮的隐藏动画.此外,在单元格高度== 0的情况下,正确处理自动布局.

更多信息在我的博客(俄语)


Gar*_*rke 8

是的,这绝对是可能的,尽管我现在正在努力解决同样的问题.我设法让细胞隐藏起来,一切正常,但我目前无法整齐地制作动画.这是我发现的:

我根据第一部分第一行中ON/OFF开关的状态隐藏行.如果开关打开,则同一部分下方有1行,否则有2行不同.

切换开关时我有一个选择器,我设置一个变量来指示我所处的状态.然后我调用:

[[self tableView] reloadData];
Run Code Online (Sandbox Code Playgroud)

我重写了tableView:willDisplayCell:forRowAtIndexPath: function,如果该单元应该被隐藏,我这样做:

[cell setHidden:YES];
Run Code Online (Sandbox Code Playgroud)

隐藏单元格及其内容,但不会删除它占用的空间.

要删除空格,请覆盖tableView:heightForRowAtIndexPath:函数,并为应隐藏的行返回0.

您还需要覆盖tableView:numberOfRowsInSection:并返回该部分中的行数.你必须在这里做一些奇怪的事情,这样如果你的桌子是一个分组的样式,圆角就会出现在正确的单元格上.在我的静态表中,该部分有完整的单元格集,因此第一个单元格包含该选项,然后是1个单元格用于ON状态选项,另外2个单元格用于OFF状态选项,总共4个单元格.当选项为ON时,我必须返回4,这包括隐藏选项,以便显示的最后一个选项有一个圆角框.当选项关闭时,最后两个选项不会显示,所以我返回2.这一切都让人觉得笨重.对不起,如果这不是很清楚,描述起来很棘手.为了说明设置,这是IB中表格部分的构造:

  • 第0行:带ON/OFF开关的选项
  • 第1行:选项为ON时显示
  • 第2行:选项为OFF时显示
  • 第3行:选项为OFF时显示

因此,当选项为ON时,表格将报告两行:

  • 第0行:带ON/OFF开关的选项
  • 第1行:选项为ON时显示

当选项为OFF时,表格将报告四行:

  • 第0行:带ON/OFF开关的选项
  • 第1行:选项为ON时显示
  • 第2行:选项为OFF时显示
  • 第3行:选项为OFF时显示

这种方法由于几个原因感觉不正确,就目前为止我的实验而言,所以如果你找到更好的方法,请告诉我.到目前为止我观察到的问题是:

  • 告诉表格行数与底层数据中可能包含的行数不同,这是错误的.

  • 我似乎无法为变化制作动画.我已经尝试过使用tableView:reloadSections:withRowAnimation:而不是reloadData,结果似乎没有意义,我仍然试图让它工作.目前似乎发生的事情是tableView没有更新正确的行,所以一个仍然隐藏应该显示,并在第一行下面留空.我认为这可能与关于基础数据的第一点有关.

希望有人能够建议替代方法或者如何扩展动画,但也许这会让你开始.我很抱歉缺少功能的超链接,我把它们放进去但是被垃圾邮件过滤器拒绝了,因为我是一个相当新的用户.


Chr*_*bst 7

根据Justas的回答,但对于Swift 4:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    let cell = super.tableView(tableView, cellForRowAt: indexPath)

    if cell == self.cellYouWantToHide {
        return 0
    }

    return super.tableView(tableView, heightForRowAt: indexPath)
}
Run Code Online (Sandbox Code Playgroud)


Jun*_*Gan 7

好的,经过一些尝试,我得到了一个不常见的答案。我正在使用“isHidden”或“hidden”变量来检查是否应隐藏此单元格。

  1. 为您的视图控制器创建一个 IBOutlet。 @IBOutlet weak var myCell: UITableViewCell!

  2. 更新myCell自定义函数中的 ,例如您可以在 viewDidLoad 中添加它:

override func viewDidLoad() { super.viewDidLoad() self.myCell.isHidden = true }

  1. 在您的委托方法中:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let cell = super.tableView(tableView, cellForRowAt: indexPath) guard !cell.isHidden else { return 0 } return super.tableView(tableView, heightForRowAt: indexPath) }

这将减少您在委托方法中的逻辑,您只需要专注于您的业务需求。


rob*_*ers 6

简单的 iOS 11 和 IB/Storyboard 兼容方法

对于 iOS 11,我发现Mohamed Saleh 答案的修改版本效果最好,并根据 Apple 文档进行了一些改进。它的动画效果很好,避免了任何丑陋的黑客或硬编码值,并使用已在 Interface Builder 中设置的行高

基本概念是将所有隐藏行的行高设置为 0。然后用于tableView.performBatchUpdates触发一致工作的动画。

设置单元格高度

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if indexPath == indexPathOfHiddenCell {
        if cellIsHidden {
            return 0
        }
    }
    // Calling super will use the height set in your storyboard, avoiding hardcoded values
    return super.tableView(tableView, heightForRowAt: indexPath)
}
Run Code Online (Sandbox Code Playgroud)

您需要确保cellIsHiddenindexPathOfHiddenCell根据您的用例进行适当的设置。对于我的代码,它们是我的表视图控制器上的属性。

切换单元格

在任何控制可见性的方法中(可能是按钮操作或didSelectRow),在块内切换 cellIsHidden 状态performBatchUpdates

tableView.performBatchUpdates({
                // Use self to capture for block
                self.cellIsHidden = !self.cellIsHidden 
            }, completion: nil)
Run Code Online (Sandbox Code Playgroud)

Apple 建议performBatchUpdatesbeginUpdatesendUpdates尽可能使用 over / 。


Dav*_*rta 5

由于自动布局问题,上述隐藏/显示单元格、更改 rowHeight 或弄乱自动布局约束的答案对我不起作用。代码变得无法忍受。

对于一个简单的静态表,最适合我的是:

  1. 为静态表中的每个单元格创建一个出口
  2. 创建仅包含要显示的单元格出口的数组
  3. 覆盖 cellForRowAtIndexPath 以从数组中返回单元格
  4. 覆盖 numberOfRowsInSection 以返回数组的计数
  5. 实现一个方法来确定该数组中需要哪些单元格,并在需要时调用该方法,然后重新加载数据。

这是我的表视图控制器中的一个示例:

@IBOutlet weak var titleCell: UITableViewCell!
@IBOutlet weak var nagCell: UITableViewCell!
@IBOutlet weak var categoryCell: UITableViewCell!

var cellsToShow: [UITableViewCell] = []

override func viewDidLoad() {
    super.viewDidLoad()
    determinCellsToShow()
}

func determinCellsToShow() {
    if detail!.duration.type != nil {
        cellsToShow = [titleCell, nagCell, categoryCell]
    }
    else {
        cellsToShow = [titleCell,  categoryCell]
    }
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    return cellsToShow[indexPath.row]
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return cellsToShow.count
}
Run Code Online (Sandbox Code Playgroud)