UITableViewCell Separator在iOS7中消失了


129

UITableView仅在iOS 7中有一些奇怪的问题。

UITableViewCellSeparator在第一行上方和最后一行下方消失。有时在选择行或某些滚动操作之后,它会出现。

在我的情况下tableView是从Storyboardwith UITableViewStylePlain样式加载的。问题肯定不在UITableViewCellSeparatorStyle,默认情况下不会更改UITableViewCellSeparatorStyleSingleLine

正如我在Apple Dev论坛此处此处)所读的,其他人也有这种问题,并且找到了一些解决方法,例如:

Workaround: disable the default selection and recreate the behaviour in a method
trigged by a tapGestureRecognizer.

但是我仍在寻找这种分隔符奇怪行为的原因。

有任何想法吗?

更新:正如我在XCode 5.1 DP和iOS 7.1 beta中所看到的,Apple试图解决此问题。现在,有时需要刷新,但有时在创建表视图之后,有时会在最后一行下方显示所需的分隔符。


19
有时候我觉得iOS7太不成熟了。这和我在此处列出的一长串内容显示了iOS7与以前的iOS的完善程度之间的距离。
Bms270 2013年

1
Apple的“设置”应用存在相同的问题,因此我认为这是iOS 7问题。我已经报告了该问题,在他们的后续行动中,他们要求提供显示该问题的图像。
2013年

@Gatada您在“设置”应用中的哪里看到了问题?
Jamie Forrest

2
遇到相同的问题,并通过添加[cell setSeparatorInset:UIEdgeInsetsFromString(@“ 1”)]进行了修复;在(UITableViewCell *)tableView:(UITableView *)tableView中使用cellForRowAtIndexPath:(NSIndexPath *)indexPath方法
Ayesha Fatima

1
这似乎在iOS 8.0 Beta中已被打破。我已经打开雷达17724043,如果有人愿意将其翻折。
安迪·威尔金森

Answers:


77

我转储了受影响单元格的子视图层次结构,并发现将其_UITableViewCellSeparatorView设置为隐藏。难怪它没有显示!

我覆盖layoutSubviews了我的UITableViewCell子类,现在分隔符已可靠显示:

目标-C

- (void)layoutSubviews {
    [super layoutSubviews];

    for (UIView *subview in self.contentView.superview.subviews) {
        if ([NSStringFromClass(subview.class) hasSuffix:@"SeparatorView"]) {
            subview.hidden = NO;
        }
    }
}

斯威夫特

override func layoutSubviews() {
    super.layoutSubviews()

    guard let superview = contentView.superview else {
        return
    }
    for subview in superview.subviews {
        if String(subview.dynamicType).hasSuffix("SeparatorView") {
            subview.hidden = false
        }
    }
}

这里提出的其他解决方案对我而言并不一致,或者显得笨拙(添加自定义1 px页脚视图)。


我已经使用了您的方法,但没有在子类中为UITableViewCell添加类别。它帮助了我。谢谢。
Vitalii Boiarskyi 2014年

当然也可以归类。由于这需要大量方法,因此我选择了简单的子类方法。
Ortwin Gentz 2014年

3
这是唯一对我有用的解决方案。当UITableView有足够的行可滚动时,我的问题突然出现了。但是,“ hasSuffix:”部分非常脆弱。
juhan_h 2014年

为我工作。也许问题很愚蠢,但这不是使用Private API吗?
Foriger

1
在iOS9中为我工作
tounaobun 2015年

42

这对我有用:

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

    // fix for separators bug in iOS 7
    self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;

您应该获得更多的赞成票,这正是我所需要的,我将其添加到类别中,所以现在在执行[tableView reloadData]之后,如果发生此错误(在我的情况下,重新显示了隐藏的页脚分隔符),我打电话[tableView reloadSeparators];
NiñoScript

12

我还与缺少分离的问题,我发现,当问题发生仅heightForRowAtIndexPath返回一个十进制数。解:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return ceil(yourHeight) // Ceiling this value fixes disappearing separators
}

1
如果我什至没有实现heightForRowAtIndexPath方法,该怎么办?
BS13年

有趣。但是,它也发生在我的项目中-而且heightForRowAtIndexPath没有实现。在IB行/单元格中的高度为100。–
Johan

6
它没有为我解决问题,并且我有heightForRowAtIndexPath方法。
Gergely Kovacs 2013年

至少部分解决此问题。该单元似乎确实对非整数高度感到困惑。
尼克·弗罗洛夫

您需要确定像素数,然后除以屏幕比例因子以获得点数,该点数可以是十进制。如果不这样做,将不会进行视网膜优化。
trss 2014年

11

您是否尝试过在表的页眉和页脚中使用浅灰色背景色添加高度为1的UIView?基本上,它将模拟第一个和最后一个分隔符。


这听起来不错,但是您如何做到这一点呢?使用viewforfooter?
skinsfan00atg

1
听起来并不容易,出于某种原因,我还必须实现heightforfooter,否则initwithframe中指定的高度将被忽略。我还注意到,尽管lightgraycolor不是相同的颜色,所以这将不会真正起作用。可能必须制作自定义灰色
skinsfan00atg 2013年

那真是个简陋的IMO
JackyJohnson,2014年

是的,我完全同意。这是我在iOS 7发行后立即发现的唯一解决方法,现在有没有更好的方法?
airpaulg 2014年

2
要匹配颜色,请使用tableView.separatorColor
Jeff

8

@samvermette

我使用此委托方法解决了该问题。现在它不会闪烁:

-(void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath {
    // fix for separators bug in iOS 7
    tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
}


-(void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath {
    // fix for separators bug in iOS 7
    tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
}

7

我们在应用程序中遇到了此问题。当用户选择一个单元格时,新的表格视图被推到导航控制器堆栈上,然后当用户将其弹出时,分隔符丢失了。我们通过把解决它[self.tableView deselectRowAtIndexPath:indexPath animated:NO];didSelectRowAtIndexPath表视图委托方法。


你试过了self.tableView.allowsMultipleSelection = NO;吗?
wrightak

6

如果需要的话,这是一种更容易的解决方法(尽管有点混乱),请在重新加载数据并完成默认动画后尝试选择和取消选择单元格。

只需添加:

[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[self.tableView deselectRowAtIndexPath:indexPath animated:NO];

1
这个解决方法为我解决了这个问题;但是,我撤消了它-保留了选择。因此,首先取消选择,然后重新选择。
约翰·

2
当您说“重新加载数据之后”时,我不知道该覆盖什么
airpaulg

6

我发现最简单的解决方案是,在重新加载单元格之后,还要重新加载上面的单元格:

if (indexPath.row > 0) {
    NSIndexPath *path = [NSIndexPath indexPathForRow:indexPath.row - 1 inSection:indexPath.section];
    [self.tableView reloadRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationNone];
}

1
为我工作。谢谢。任何其他方法都无济于事(包括更改samvermette答案中的分隔符样式)。
surfrider 2014年

4

一个既简单又干净的解决方案,可在iOS 8和iOS 9(测试版1)上使用

这是一个简单,干净且无干扰的解决方法。它涉及调用将修复分隔符的类别方法。

您所需要做的就是遍历单元格的层次结构并取消隐藏分隔符。像这样:

for (UIView *subview in cell.contentView.superview.subviews) {
    if ([NSStringFromClass(subview.class) hasSuffix:@"SeparatorView"]) {
        subview.hidden = NO;
    }
}

我建议将其添加到UITableViewCell的类别中,如下所示:

@interface UITableViewCell (fixSeparator)
- (void)fixSeparator;
@end

@implementation UITableViewCell (fixSeparator)

- (void)fixSeparator {
    for (UIView *subview in self.contentView.superview.subviews) {
        if ([NSStringFromClass(subview.class) hasSuffix:@"SeparatorView"]) {
            subview.hidden = NO;
        }
    }
}

@end

由于分隔符可以在与当前选定单元格不同的单元格中消失,因此最好在表视图的所有单元格中调用此修复程序。为此,您可以向UITableView添加如下类别:

@implementation UITableView (fixSeparators)

- (void)fixSeparators {
    for (UITableViewCell *cell in self.visibleCells) {
        [cell fixSeparator];
    }
}

@end

有了这个,您可以-fixSeparatos在导致它们消失的操作之后立即调用tableView。就我而言,是在致电[tableView beginUpdates]和之后[tableView endUpdates]

正如我在开始时所说的,我已经在iOS 8和iOS 9上进行了测试。我想它甚至可以在iOS 7上运行,但是我没有办法在那儿尝试。如您所知,这确实弄乱了单元的内部,因此在将来的某些发行版中可能会停止工作。从理论上讲,Apple可以(因此有0.001%的机会)拒绝您的应用程序,但是我看不到他们怎么能知道您在做什么(检查类的后缀不能被静态分析器检测到)糟糕,IMO)。


是。在转换到最新版本的Swift之后,这对我有用。++,用于在每次调用tableView.endUpdates()之后调用fixSeparators。
ObjectiveTC

1
很高兴这对您有所帮助!我真的相信这是最好的解决方案,因此希望更多的人会发现它。
Lukas Petr

4

根据@ ortwin-gentz的评论,此解决方案适用于iOS 9。

func fixCellsSeparator() {

    // Loop through every cell in the tableview
    for cell: UITableViewCell in self.tableView.visibleCells {

        // Loop through every subview in the cell which its class is kind of SeparatorView
        for subview: UIView in (cell.contentView.superview?.subviews)!
            where NSStringFromClass(subview.classForCoder).hasSuffix("SeparatorView") {
                subview.hidden = false
        }
    }

}

(SWIFT代码)

我在tableView的某些方法中调用endUpdates()后使用了FixCellsSeparator()函数,例如:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

    //  Perform some stuff
    //  ...

    self.tableView.endUpdates()
    self.fixCellsSeparator()

}

我希望此解决方案对某人有帮助!


2

补充airpaulg的答案。

因此,基本上一个必须实现两个UITableDelegate方法。这是我的解决方案,可在iOS7和iOS6上使用。

#define IS_OS_VERSION_7 (NSFoundationVersionNumber_iOS_6_1 < floor(NSFoundationVersionNumber))

#define UIColorFromRGB(hexRGBValue) [UIColor colorWithRed:((float)((hexRGBValue & 0xFF0000) >> 16))/255.0 green:((float)((hexRGBValue & 0xFF00) >> 8))/255.0 blue:((float)(hexRGBValue & 0xFF))/255.0 alpha:1.0]

//如果单元格不能覆盖整个屏幕,这将隐藏单元格下方的空白表格网格

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
    UIView *view = nil;

    if (IS_OS_VERSION_7 /* && <is this the last section of the data source> */)
    {
        CGFloat height = 1 / [UIScreen mainScreen].scale;
        view = [[UIView alloc] initWithFrame:CGRectMake(0., 0., 320., height)];
        view.backgroundColor = UIColorFromRGB(0xC8C7CC);
        view.autoresizingMask = UIViewAutoresizingFlexibleWidth;
    }
    else
    {
        view = [UIView new];
    }
    return view;
}


- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
    if (IS_OS_VERSION_7 /* && <is this the last section of the data source> */)
    {
        return 1 / [UIScreen mainScreen].scale;
    }
    else
    {
        // This will hide the empty table grid below the cells if they do not cover the entire screen
        return 0.01f;
    }
}

2

这为我解决了这个问题:

确保clipsToBounds将单元格的设置为YES,将单元格的设置为NO contentView。也设置cell.contentView.backgroundColor = [UIColor clearColor];


在单元和contentView的情节提要中将clipSubviews设置为false可以解决此问题!需要更多的投票!
布雷特

在我的单元格上将clipsToBounds设置为YES就足够了。
Rene Juuse

2

似乎在许多情况下都会出现此问题。

对我来说,这与细胞选择有关。我不知道为什么,也没有时间深入研究它,但是我可以说当我将单元格设置selectionStyle为无时开始发生。即:

//This line brought up the issue for me
cell.selectionStyle = UITableViewCellSelectionStyleNone;

我尝试使用上面的某些委托方法来打开和关闭的separatorStyle 属性,tableView但它们似乎并没有采取任何措施来解决我的问题。

我之所以需要它的全部原因是因为我什至不需要细胞选择。

因此,我确实找到了对我有用的东西。我只是在上禁用了选择UITableView

tableView.allowsSelection = NO;

希望这对某人有帮助,如果您不需要选择单元格。


2

我尝试了很多建议以解决问题,但无法解决问题。最后我决定自定义分隔线,如下所示:

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    var lineView = UIView(frame: CGRectMake(20, cell.contentView.frame.size.height - 1.0, cell.contentView.frame.size.width - 20, 1))

    lineView.backgroundColor = UIColor(red: 170.0/255.0, green: 170.0/255.0, blue: 170.0/255.0, alpha: 1)
    cell.contentView.addSubview(lineView)
}

P / S:willDisplayCell上自定义,不是cellForRowAtIndexPath


1

令人惊讶的是,separatorInset来回更改单元格的值似乎是可行的:

NSIndexPath *selectedPath = [self.controller.tableView indexPathForSelectedRow];
[self.controller.tableView deselectRowAtIndexPath:selectedPath animated:YES];

UITableViewCell *cell = [self.controller.tableView cellForRowAtIndexPath:selectedPath];
UIEdgeInsets insets = cell.separatorInset;
cell.separatorInset = UIEdgeInsetsMake(0.0, insets.left + 1.0, 0.0, 0.0);
cell.separatorInset = insets;

1

我还遇到了UITableView底部的分隔线显示不一致的问题。使用自定义页脚视图,我已经能够创建类似的行并将其显示在潜在的现有行(有时会丢失)的顶部。

此解决方案的唯一问题是Apple可能会在一天之内更改生产线的厚度

- (UIView*) tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
    float w = tableView.frame.size.width;

    UIView * footerView =  [[UIView alloc] initWithFrame:CGRectMake(0, 0, w, 1)];
    footerView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
    footerView.clipsToBounds=NO;

    UIView* separatoraddon = [[UIView alloc] initWithFrame:CGRectMake(0, -.5, w, .5)];
    separatoraddon.autoresizingMask = UIViewAutoresizingFlexibleWidth;
    separatoraddon.backgroundColor = tableView.separatorColor;
    [footerView addSubview:separatoraddon];

    return footerView;
}
- (CGFloat) tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
    return 1;
}

1

我解决了将以下代码行放在发生tableview更新的地方的问题:

self.tableView.separatorStyle = UITableViewCellSeparatorStyle.None;
self.tableView.separatorStyle = UITableViewCellSeparatorStyle.SingleLine;

例如,就我而言,我将它们放在这里:

tableView.beginUpdates()
tableView.insertRowsAtIndexPaths(insertIndexPaths, withRowAnimation: UITableViewRowAnimation.Fade)
tableView.endUpdates()
self.tableView.separatorStyle = UITableViewCellSeparatorStyle.None;
self.tableView.separatorStyle = UITableViewCellSeparatorStyle.SingleLine;

1

您需要先删除单元格选择,然后再进行单元格更新。然后,您可以恢复选择。

NSIndexPath *selectedPath = [self.tableview indexPathForSelectedRow];
    [self.tableview deselectRowAtIndexPath:selectedPath animated:NO];
    [self.tableview reloadRowsAtIndexPaths:@[ path ] withRowAnimation:UITableViewRowAnimationNone];
    [self.tableview selectRowAtIndexPath:selectedPath animated:NO scrollPosition:UITableViewScrollPositionNone];

1

要解决以上问题,请在viewDidLoad中添加空白页脚。

UIView *emptyView_ = [[UIView alloc] initWithFrame:CGRectZero];   
emptyView_.backgroundColor = [UIColor clearColor];  
[tableView setTableFooterView:emptyView_];

请不要在viewForFooterInSection委托方法中使用以上几行。表示不要实现viewForFooterInSection方法。


0

扩大关于airpaulg的答案,因为它并不完全适合我。

我还必须实现heightforfooter方法来获取高度

然后我发现浅灰色太暗了。我通过获取当前的tableview分隔符颜色并使用它来解决此问题:

UIColor *separatorGray = [self.briefcaseTableView separatorColor]; [footerSeparator setBackgroundColor:separatorGray];


0

我在我们的项目中也遇到了这个问题。我的表格视图有一个tableFooterView,可以实现此目的。我发现如果删除tableFooterView,将在最后一行下面显示分隔符。


0

对我来说与众不同的是重新加载了底部分隔线不会出现的行:

NSIndexPath *indexPath = 
     [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex];  
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];

0

我用另一种方式解决了这个问题:在tableview.tableFooterView中添加一个高度为0.5px且颜色为浅灰色的图层作为其子图层。

代码是这样的:

UIView *tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 70)];
CALayer *topSeperatorLine = [CALayer layer];
topSeperatorLine.borderWidth = 0.5f;
topSeperatorLine.borderColor = [UIColor lightGrayColor].CGColor;
topSeperatorLine.frame = CGRectMake(0, 0, 320, 0.5f);
[tableFooterView.layer addSublayer:topSeperatorLine];
self.tableView.tableFooterView = tableFooterView;

0

在您的UITableViewCell子类中,实现layoutSubviews并添加:

- (void)layoutSubviews{
    [super layoutSubviews]
    for (UIView *subview in self.contentView.superview.subviews) {
        if ([NSStringFromClass(subview.class) hasSuffix:@"SeparatorView"]) {
            CGRect separatorFrame = subview.frame;
            separatorFrame.size.width = self.frame.size.width;
            subview.frame = separatorFrame;
        }
    }
}

0

正如其他人所提到的,这个问题似乎以多种方式表现出来。我通过以下解决方法解决了我的问题:

[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[tableView endUpdates];

0

通过回答和解决方案,我得出了以下观察结果:

在iOS 7和8中,这似乎都是有问题的。从代码中选择单元格时,我注意到了问题。 viewDidLoad方法中的因此:

1)解决方法是在viewDidAppear:有人不介意呈现视图和选择单元格之间出现明显延迟的情况下这样做

2)第二个解决方案对我来说很有效,但是代码基于它的内部实现而显得有些脆弱。UITableViewCell

3)添加自己的分隔符似乎是目前最灵活,最有效的方法,但需要更多编码:)


0

设置样式viewWillAppear对我有用。


0

由于这仍然是IOS 8的问题,因此我将在Swift中添加解决方案。将tableview分隔线设置为none。然后将此代码添加到cellForRowAtIndexPath委托方法。它将添加一个不错的分隔符。if语句允许确定哪些单元格应具有分隔符。

    var separator:UIView!
    if let s = cell.viewWithTag(1000)
    {
        separator = s
    }
    else
    {
        separator = UIView()
        separator.tag = 1000
        separator.setTranslatesAutoresizingMaskIntoConstraints(false)
        cell.addSubview(separator)

        // Swiper constraints
        var leadingConstraint = NSLayoutConstraint(item: separator, attribute: .Leading, relatedBy: .Equal, toItem: cell, attribute: .Leading, multiplier: 1, constant: 15)
        var heightConstraint = NSLayoutConstraint(item: separator, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 0.5)
        var bottomConstraint = NSLayoutConstraint(item: cell, attribute: .Bottom, relatedBy: .Equal, toItem: separator, attribute: .Bottom, multiplier: 1, constant:0)
        var trailingConstraint = NSLayoutConstraint(item: cell, attribute: .Trailing, relatedBy: .Equal, toItem: separator, attribute: .Trailing, multiplier: 1, constant: 15)
        cell.addConstraints([bottomConstraint, leadingConstraint, heightConstraint, trailingConstraint])
    }

    if indexPath.row == 3
    {
        separator.backgroundColor = UIColor.clearColor()
    }
    else
    {
        separator.backgroundColor = UIColor.blackColor()
    }

0

这是我对这个问题的解决方案。插入单元格后,请重新加载该部分。如果重新加载整个部分对您来说太繁琐,则只需重新加载上方和下方的indexPath。

[CATransaction begin];
[CATransaction setCompletionBlock:^{
    //  Fix for issue where seperators disappear

    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
}];

[self.tableView insertRowsAtIndexPaths:@[ indexPath ] withRowAnimation:UITableViewRowAnimationFade];

[CATransaction commit];

0

遇到类似的问题,我发现另一个好的解决方案,尤其是当您的dataSource不大时,是在实现该-(void)scrollViewDidScroll:(UIScrollView *)scrollView方法时重新加载tableData ,这是一个示例:

-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
      if (scrollView == self.tableView1) {
          [self.tableView1 reloadData];
      }

      else {
          [self.tableView2 reloadData];
      }
}

您也可以仅基于可见的dataSource重新加载不可见的数据,但这需要更多的技巧。

请记住,此委托函数属于 UITableViewDelegate协议范围!

希望能帮助到你!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.