IOS 7でのみUITableView
に奇妙な問題があります。
UITableViewCellSeparator
は、最初の行の上と最後の行の下に消えます。行またはいくつかのスクロールアクションを選択した後に表示される場合があります。
私の場合、tableView
はStoryboard
スタイルからUITableViewStylePlain
からロードされます。問題は確実にUITableViewCellSeparatorStyle
にはなく、デフォルトのUITableViewCellSeparatorStyleSingleLine
から変更されません。
Apple Dev Forums(ここで読むと および ここ)他の人がこのような問題を抱えており、次のような回避策が見つかりました:
Workaround: disable the default selection and recreate the behaviour in a method
trigged by a tapGestureRecognizer.
しかし、私はまだそのような分離器の奇妙な振る舞いの理由を探しています。
何か案は?
更新:XCode 5.1 DPおよびiOS 7.1ベータ版で見たように、Appleはこの問題を修正しようとしました。必要に応じて、テーブルビューの作成後ではなく、リフレッシュ後、最後の行の下にセパレータが表示される場合があります。
影響を受けるセルのサブビュー階層をダンプしたところ、_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;
}
}
}
Swift:
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ピクセルフッタービューを追加)。
これは私のために働いた:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// fix for separators bug in iOS 7
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
また、区切り文字が欠落しているという問題もあり、問題はheightForRowAtIndexPath
が10進数を返すの場合にのみ発生することがわかりました。解決:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return ceil(yourHeight) // Ceiling this value fixes disappearing separators
}
背景の色が明るい灰色のテーブルのヘッダーとフッターに高さ1のUIViewを追加しようとしましたか?基本的に、最初と最後のセパレーターをモックします。
なかむら
このデリゲートメソッドを使用して問題を修正しました。今ではちらつきません:
-(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;
}
アプリでこの問題が発生しました。ユーザーがセルを選択すると、新しいテーブルビューがNavigation Controllerスタックにプッシュされ、ユーザーがそれをポップオフすると、セパレータが欠落していました。 didSelectRowAtIndexPath
テーブルビューデリゲートメソッドに[self.tableView deselectRowAtIndexPath:indexPath animated:NO];
を入れることで解決しました。
最も簡単な解決策は、セルをリロードした後、上のセルもリロードすることです。
if (indexPath.row > 0) {
NSIndexPath *path = [NSIndexPath indexPathForRow:indexPath.row - 1 inSection:indexPath.section];
[self.tableView reloadRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationNone];
}
必要な場合は簡単な(少し厄介ですが)回避策があります。データをリロードしてデフォルトのアニメーションが完了したら、セルの選択と選択解除を試してください。
追加するだけです:
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[self.tableView deselectRowAtIndexPath:indexPath animated:NO];
@ 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()
}
このソリューションが誰かに役立つことを願っています!
以下に、単純なcleanおよび非侵入型の回避策を示します。区切り文字を修正するカテゴリメソッドを呼び出す必要があります。
必要なことは、セルの階層をたどってセパレータを非表示にすることだけです。このような:
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
現在選択されているセルとは異なるセルでセパレータが消えることがあるため、テーブルビューのallセルでこの修正を呼び出すことをお勧めします。そのために、次のようなUITableViewにカテゴリを追加できます。
@implementation UITableView (fixSeparators)
- (void)fixSeparators {
for (UITableViewCell *cell in self.visibleCells) {
[cell fixSeparator];
}
}
@end
これを適切に設定すると、tableViewで-fixSeparatos
を呼び出すアクションの直後にそれらを非表示にすることができます。私の場合、[tableView beginUpdates]
と[tableView endUpdates]
を呼び出した後です。
冒頭で述べたように、私はiOS 8とiOS 9の両方でこれをテストしました。iOS7でも動作するものと思われますが、そこで試す方法はありません。おそらくご存知のように、これはセルの内部をいじるので、将来のリリースで動作しなくなる可能性があります。 Appleは理論的には(0.001%の確率で)あなたのアプリを拒否する可能性がありますが、あなたがそこで何をしているのかを見つけることさえできません(クラスのサフィックスをチェックすることはできません)静的アナライザーによって何か悪いものとして検出された、IMO)。
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;
}
}
私は修正するために非常に多くの提案を試みましたが、それを修正することはできません。
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 NOT cellForRowAtIndexPath
この問題は非常に多くの状況下で現れるようです。
私にとっては、セルの選択と関係がありました。理由がわからず、深く掘り下げる時間がありませんでしたが、セルのselectionStyle
をnoneに設定したときに発生し始めたと言えます。すなわち:
//This line brought up the issue for me
cell.selectionStyle = UITableViewCellSelectionStyleNone;
separatorStyle
のtableView
プロパティのオンとオフを切り替える上記のデリゲートメソッドのいくつかを使用してみましたが、問題を解決するために何もしなかったようです。
私がそれを必要とした全体の理由は、セルを選択する必要さえなかったからです。
だから、私は私のために働く何かを見つけました。 UITableView
で選択を無効にしました:
tableView.allowsSelection = NO;
セルを選択する必要がない場合、これが誰かの助けになることを願っています。
これは私のために問題を修正しました:
セルに対してclipsToBounds
がYESに設定されているが、セルのcontentView
に対してNOに設定されていることを確認してください。また、cell.contentView.backgroundColor = [UIColor clearColor];
を設定します
セルの更新を行う前に、セルの選択を削除する必要があります。その後、選択を復元できます。
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];
驚くべきことに、セルの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;
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;
}
上記の問題を修正するには、viewDidLoadに空のフッターを追加します。
UIView *emptyView_ = [[UIView alloc] initWithFrame:CGRectZero];
emptyView_.backgroundColor = [UIColor clearColor];
[tableView setTableFooterView:emptyView_];
ViewForFooterInSectionデリゲートメソッドで上記の行を使用しないでください。意味viewForFooterInSectionメソッドを実装しないでください。
テーブルビューの更新が発生する場所に次のコード行を配置することを解決しました:
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;
この問題に対する私の解決策は次のとおりです。セルを挿入したら、セクションをリロードします。セクション全体の再読み込みが集中的すぎる場合は、indexPathsを上下に再読み込みするだけです。
[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];
同様の問題にぶつかりました。特にdataSourceが大きくない場合は、-(void)scrollViewDidScroll:(UIScrollView *)scrollView
メソッドを実装するときにtableDataをリロードするのがもう1つの良い解決策であることがわかりました。例を次に示します。
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == self.tableView1) {
[self.tableView1 reloadData];
}
else {
[self.tableView2 reloadData];
}
}
また、目に見えるdataSourceに基づいて目に見えないデータをリロードすることもできますが、それにはさらにハッキングが必要です。
このデリゲート関数はUITableViewDelegate
プロトコルに該当することに注意してください!
それが役に立てば幸い!
別の方法でこの問題を解決します。高さが0.5ピクセルで、色が薄いグレーのレイヤーをサブレイヤーとして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;
私にとってうまくいったのは、beginUpdatesとendUpdatesの後にデータをリロードすることでした:
private func animateCellHeighChangeForTableView(tableView: UITableView, withDuration duration: Double) {
UIView.animateWithDuration(duration) { () -> Void in
tableView.beginUpdates();
tableView.endUpdates();
tableView.reloadData();
}
}
私の場合、記載されているすべての回答を試しましたが、私には役に立たなかったので、2つの回答を使用しました。以下が解決策です。
uITableViewConstrollerに以下の行を追加します
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// fix for separators bug in iOS 7
self.tableView.separatorStyle = .none;
self.tableView.separatorStyle = .singleLine;
}
次に、UITableViewCellで以下のメソッドをオーバーライドします
override func layoutSubviews() {
super.layoutSubviews()
guard let superview = contentView.superview else {
return
}
for subview in superview.subviews {
if String(describing: type(of: subview)).hasSuffix("SeparatorView") {
subview.isHidden = false
}
}
}
これが他の人にも役立つことを願っています。ありがとう
@Leeの回答を変更して、デフォルトのテーブルビューセパレータースタイルを取得し、最後の行に奇数のセパレーターを表示しないようにしました。それが誰かを助けることを願っています。
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row >= yourDataSource.count - 1{
return
}
let lineView = UIView(frame: CGRectMake(20, cell.contentView.frame.size.height - 1.0, cell.contentView.frame.size.width, 0.5))
lineView.backgroundColor = tableView.separatorColor
cell.contentView.addSubview(lineView)
}
シンプルで、クリーンで、効率的な方法セルのすべてのサブビューをループすることなくこれを行うには、セルがデキュー/作成されるたびに呼び出され、適切なole KVCを使用します。
UIView *separatorView = [self valueForKey:@"separatorView"]; // pull separator using KVC
if (separatorView) {
separatorView.hidden = NO;
}
それをセルクラスのlayoutSubviews
に固定すると、問題を解決できます。
答えと解決策を見て、私はそのような観察を思いつきました:
これはiOS 7と8の両方で問題があるようです。viewDidLoad
メソッドのコードからセルを選択するときに問題に気付きました。
1)ビューを表示してからセルを選択するまでの顕著な遅延を誰かが気にしない場合、回避策はviewDidAppear:
でそうしていました
2) 2番目の解決策 私にとってはうまくいきましたが、UITableViewCell
の内部実装に基づいているため、コードは少し脆弱に見えます
3)独自のセパレータを追加することは、今のところ最も柔軟性があり、最高のようですが、より多くのコーディングが必要です:)
これはIOS 8の問題であるため、Swiftにソリューションを追加します。テーブルビューの区切り線をなしに設定します。次に、このコードをcellForRowAtIndexPathデリゲートメソッドに追加します。 Niceセパレーターが追加されます。 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()
}
airpaulgの回答を拡張するために、それは私のために完全に機能しなかったので..
高さを取得するにはheightforfooterメソッドも実装する必要がありました
それから、明るい灰色が濃すぎることに気付きました。テーブルビューの現在のセパレーターの色を取得し、それを使用してこれを修正しました:
UIColor *separatorGray = [self.briefcaseTableView separatorColor]; [footerSeparator setBackgroundColor:separatorGray];
アニメーションで行を挿入/削除すると、セパレーターが消える問題が発生します。私にとってそれを解決したのは、deselectRowAtIndexPath()
を呼び出すことでした
var insertion = [NSIndexPath]()
var deletion = [NSIndexPath]()
// TODO: populate the insertion array and deletion array
tableView.beginUpdates()
tableView.deleteRowsAtIndexPaths(deletion, withRowAnimation: .Fade)
tableView.insertRowsAtIndexPaths(insertion, withRowAnimation: .Fade)
if let indexPath = tableView.indexPathForSelectedRow {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
tableView.endUpdates()
私たちのプロジェクトでもこの問題に遭遇しました。私のテーブルビューには、これを実現できるtableFooterViewがありました。 tableFooterViewを削除すると、最後の行の下にセパレータが表示されることがわかりました。
他の誰かが言ったように、この問題はさまざまな形で現れるようです。次の回避策で問題を解決しました。
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[tableView endUpdates];
私にとって違いをもたらしたのは、下の区切り線が表示されない行を再ロードすることでした:
NSIndexPath *indexPath =
[NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
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;
}
}
}
viewWillAppear
でスタイルを設定するとうまくいきました。