iOS7でUITableViewCell Separatorが消える


129

UITableViewiOS 7 でのみ奇妙な問題があります。

UITableViewCellSeparator最初の行の上と最後の行の下に消えます。行またはスクロールアクションを選択した後に表示される場合があります。

私の場合tableViewStoryboardwith 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ベータで見たように、Appleはこの問題を修正しようとしました。セパレーターが必要に応じて最後の行の下に時々表示されます。更新後、テーブルビューの作成後ではありません。


19
時々私はiOS7がとても未熟だと感じます。これと私がここに持っているものの長いリストは、iOS7が以前のiOSの完成度からどれだけ離れているかを示しています。
Bms270 2013

1
Appleの設定アプリにも同じ問題があるので、これはiOS 7の問題だと思います。私はそれを報告し、彼らのフォローアップで彼らは問題を示す画像を求めました。
ヨハン

@ガタダ設定アプリのどこで問題が発生していますか?
ジェイミーフォレスト

2
同じ問題が発生し、[cell setSeparatorInset:UIEdgeInsetsFromString(@ "1")]を追加して修正しました。with(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPathメソッド
Ayesha Fatima

1
これは、iOS 8.0ベータではまだ壊れているようです。私はレーダー17724043をオープンしました。
アンディウィルキンソン

回答:


77

影響を受けるセルのサブビュー階層をダンプしたところ、_UITableViewCellSeparatorView非表示に設定されていることがわかりました。表示されないのも不思議ではありません!

私はサブクラスをオーバーライドlayoutSubviewsし、UITableViewCellセパレーターが確実に表示されるようになりました:

Objective-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ピクセルのフッタービューを追加した)。


私はあなたのメソッドを使用しましたが、サブクラスではなく、UITableViewCellにカテゴリを追加しました。それは私を助けました。ありがとう。
Vitalii Boiarskyi 14

もちろん、カテゴリ内でも可能です。これにはメソッドのスウィズリングが必要なので、シンプルなサブクラスアプローチを採用しました。
Ortwin Gentz 14

3
これは私のために働いた唯一の解決策です。UITableViewにスクロール可能な十分な行があると、問題が発生しました。ただし、「hasSuffix:」の部分は非常に脆弱です。
juhan_h 14

私のために働く。質問はばかげているかもしれませんが、これはプライベートAPIの使用ではありませんか?
外国人2015年

1
iOS9で私の作品
tounaobun

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

また、セパレーターの欠落の問題もあり、10進数を返すときheightForRowAtIndexPathにのみ問題が発生することがわかりました。解決:

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

1
heightForRowAtIndexPathメソッドも実装しない場合はどうなりますか?
BS

面白い。ただし、それは私のプロジェクトでも発生します-そして、heightForRowAtIndexPathは実装されていません。IBの行/セルの高さは100である
ヨハン

6
それは私のための問題を解決しませんでした、そして私はheightForRowAtIndexPathメソッドを持っています。
Gergely Kovacs 2013年

問題を少なくとも部分的に解決します。セルは、整数でない高さについて混乱しているようです。
Nick Frolov 2013年

ピクセル数を指定し、画面の倍率で除算して、10進数でよいポイントを取得する必要があります。これを行わないと、網膜が最適化されません。
trss 2014年

11

高さ1のUIViewを、背景が薄い灰色のテーブルのヘッダーとフッターに追加してみましたか?基本的には、最初と最後のセパレーターを模擬します。


これは良さそうに聞こえますが、どのように正確にこれを行いますか?viewforfooterを使用しますか?
skinsfan00atg 2013年

1
思ったほど簡単ではありません。何らかの理由で、heightforfooterも実装する必要がありました。そうしないと、initwithframeで指定された高さが無視されていました。また、lightgraycolorは同じ色ではないので、これは実際には機能しません。カスタムグレーを作成する必要があるかもしれません
skinsfan00atg

それはかなりぎくしゃくしたIMOです
JackyJohnson 14

ええ、私は完全に同意します。これはiOS 7のリリース直後に見つけた唯一の回避策ですが、今より良いアプローチはありますか?
airpaulg 2014

2
色を合わせるには、tableView.separatorColor
Jeff

8

ゆうた

このデリゲートメソッドを使用して問題を修正しました。今ではちらつきはありません:

-(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 2013年

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の答えからのセパレータースタイルの変更を含みます)
サーフライダー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 beginUpdates][tableView endUpdates]

冒頭で述べたように、私はこれをiOS 8とiOS 9の両方でテストしました。iOS7でも動作すると思いますが、そこで試すことはできません。おそらくご存じのとおり、これはセルの内部をいじるので、将来のリリースで機能しなくなる可能性があります。そして、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
        }
    }

}

(スウィフトコード)

tableViewのいくつかのメソッドでendUpdates()を呼び出した後、fixCellsSeparator()関数を使用します。次に例を示します。

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

    //  Perform some stuff
    //  ...

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

}

この解決策が誰かにとって役立つことを願っています!


2

airpaulgの回答を補完します。

したがって、基本的には2つの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に設定されていることを確認してくださいcontentView。また設定cell.contentView.backgroundColor = [UIColor clearColor];


セルとcontentViewの両方のストーリーボードでclipSubviewsをfalseに設定すると、問題が修正されました!賛成票がもっと必要です!
Brett

セルでclipsToBoundsをYESに設定するだけで十分でした。
Rene Juuse

2

この問題は非常に多くの状況下で明らかになるようです。

私にとって、それは細胞選択と関係がありました。なぜなのかわからず、深く掘り下げる時間もありませんでしたが、セルselectionStyleをnone に設定したときに発生し始めたと言えます。つまり:

//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

テーブルビューの更新が発生する場所にこれらのコード行を配置することを解決しました:

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メソッドも実装する必要がありました

それから私はlightgraycolorが暗すぎることに気づきました。テーブルビューの現在のセパレーターカラーをつかんでそれを使うことでこれを修正しました:

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

この問題を別の方法で解決します。高さが0.5pxで色がライトグレーのレイヤーをサブレイヤーとしてtableview.tableFooterViewに追加します。

コードは次のようになります:

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)2番目の解決策は私にとってはうまくいきましたが、コードは内部実装に基づいているため、少し壊れやすいように見えますUITableViewCell

3)独自のセパレーターを追加することは、今のところ最も柔軟で最も良いようですが、より多くのコーディングが必要です:)


0

でスタイルを設定するのがviewWillAppearうまくいきました。


0

これはまだIOS 8の問題なので、Swiftにソリューションを追加します。テーブルビューの区切り線をなしに設定します。次に、このコードを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プロトコルに該当することに注意してください。

それが役に立てば幸い!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.