UITableViewCell Separator исчезает в iOS7
У меня есть странная проблема с UITableView
только в iOS 7.
UITableViewCellSeparator
исчезает выше первой строки и ниже последней строки. Иногда после выбора строк или некоторых действий прокрутки появляется.
В моем случае tableView
загружается из стиля Storyboard
с 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 попыталась исправить эту проблему. Теперь разделитель отображается по мере необходимости иногда ниже последней строки после некоторого обновления, но не после создания таблицы.
Ответы
Ответ 1
Я сбросил иерархию подчиненных уязвимых ячеек и обнаружил, что параметр _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 px).
Ответ 2
Это сработало для меня:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// fix for separators bug in iOS 7
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
Ответ 3
У меня также возникла проблема с отсутствующим разделителем, и я узнал, что проблема возникла только тогда, когда heightForRowAtIndexPath
был , возвращая десятичное число. Решение:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return ceil(yourHeight) // Ceiling this value fixes disappearing separators
}
Ответ 4
Вы пытались добавить UIView высоты 1 в заголовок и нижний колонтитул таблицы со светло-серым цветом фона? В основном это будет издеваться над первым и последним разделителями.
Ответ 5
@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;
}
Ответ 6
Мы столкнулись с этой проблемой в нашем приложении. Когда пользователь выбрал ячейку, новое табличное представление было перенесено в стек контроллера навигатора, а затем, когда пользователь выскочил, разделитель отсутствовал. Мы решили это, положив [self.tableView deselectRowAtIndexPath:indexPath animated:NO];
в метод делегата таблицы didSelectRowAtIndexPath
.
Ответ 7
Ниже приведено более легкое (хотя и немного беспорядочное) решение, если вам это нужно, попробуйте выбрать и отменить выбор ячейки после перезагрузки данных и сделать анимацию по умолчанию.
просто добавьте:
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[self.tableView deselectRowAtIndexPath:indexPath animated:NO];
Ответ 8
Я обнаружил, что самое легкое решение - после перезагрузки ячейки также перезагрузить ячейку выше:
if (indexPath.row > 0) {
NSIndexPath *path = [NSIndexPath indexPathForRow:indexPath.row - 1 inSection:indexPath.section];
[self.tableView reloadRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationNone];
}
Ответ 9
Простое и чистое решение, которое работает как на 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
в таблицеView сразу после действия, которое приведет к их исчезновению. В моем случае это было после вызова [tableView beginUpdates]
и [tableView endUpdates]
.
Как я уже говорил в начале, я тестировал это как на iOS 8, так и на iOS 9. Я полагаю, что он будет работать даже на iOS 7, но у меня нет возможности попробовать его. Как вы, вероятно, знаете, это водится с внутренними элементами ячейки, поэтому он может перестать работать в какой-то будущей версии. И Apple теоретически (вероятность 0.001%) отклоняет ваше приложение из-за этого, но я не вижу, как они могут даже узнать, что вы там делаете (проверка суффикса класса не может быть обнаружена статическими анализаторами как нечто плохо, ИМО).
Ответ 10
Основываясь на комментарии @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)
Я использую функцию fixCellsSeparator() после вызова endUpdates() в некоторых методах моего tableView, например:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// Perform some stuff
// ...
self.tableView.endUpdates()
self.fixCellsSeparator()
}
Я надеюсь, что это решение будет полезно для кого-то!
Ответ 11
Дополнение к ответу 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;
}
}
Ответ 12
Это исправило проблему для меня:
Удостоверьтесь, что для установлено значение clipsToBounds
YES, но NO для ячейки contentView
. Также установите cell.contentView.backgroundColor = [UIColor clearColor];
Ответ 13
Похоже, этот вопрос проявляется при столь многих обстоятельствах.
Для меня это как-то связано с выбором ячейки. Я понятия не имею, почему и не успел вникнуть в нее слишком глубоко, но могу сказать, что это началось, когда я установил ячейку selectionStyle
none. то есть:
//This line brought up the issue for me
cell.selectionStyle = UITableViewCellSelectionStyleNone;
Я попытался использовать некоторые из методов делегата выше, которые включали и выключали свойство separatorStyle
для tableView
, но они, похоже, ничего не сделали, чтобы исправить мою проблему.
Вся причина, по которой я нуждался в этом, заключалась в том, что мне даже не нужен выбор ячейки.
Итак, я нашел что-то, что сработало для меня. Я просто отключил выделение на UITableView
:
tableView.allowsSelection = NO;
Надеюсь, это поможет кому-то, если вам не нужно выбирать ячейки.
Ответ 14
Я попробовал так много предложений исправить, но не могу это исправить. Наконец, я решил создать пользовательскую разделительную линию, как показано ниже:
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
Ответ 15
Совершенно удивительно, что изменение значения ячейки 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;
Ответ 16
Чтобы устранить эту проблему, добавьте пустой footer в viewDidLoad.
UIView *emptyView_ = [[UIView alloc] initWithFrame:CGRectZero];
emptyView_.backgroundColor = [UIColor clearColor];
[tableView setTableFooterView:emptyView_];
Пожалуйста, не используйте приведенные выше строки в методе делегата viewForFooterInSection. Средства не реализуют метод viewForFooterInSection.
Ответ 17
У меня также возникла проблема с этим несогласованным отображением строки разделителя в нижней части моего 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;
}
Ответ 18
Я решил разместить эти строки кода, где обновляется обновление 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;
Ответ 19
Вам нужно удалить ячейку, прежде чем выполнять обновление ячейки. Затем вы можете восстановить выбор.
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];
Ответ 20
чтобы расширить ответ на airpaulg, потому что он не полностью работает для меня..
Мне также нужно было реализовать метод heightforfooter для получения высоты
то я заметил, что lightgraycolor слишком темный. Я исправил это, захватив текущий цвет разделителя таблицы и используя это:
UIColor *separatorGray = [self.briefcaseTableView separatorColor];
[footerSeparator setBackgroundColor:separatorGray];
Ответ 21
Я также столкнулся с этой проблемой в нашем проекте. В моем представлении таблицы был tableFooterView, который мог бы сделать это. Я обнаружил, что разделитель ниже последней строки появится, если я удалю этот tableFooterView.
Ответ 22
Для меня изменилась перезагрузка строки, для которой нижняя разделительная строка не появится:
NSIndexPath *indexPath =
[NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
Ответ 23
Я решу эту проблему по-другому: добавьте слой, который имеет высоту 0.5px, а его цвет - lightgray в 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;
Ответ 24
В вашем подклассе 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;
}
}
}
Ответ 25
Как уже упоминалось, эта проблема проявляется различными способами. Я решил проблему с помощью следующего обходного пути:
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[tableView endUpdates];
Ответ 26
Пройдя ответы и решения, я придумал такие наблюдения:
Это кажется проблематичным как в iOS 7, так и в 8. Я заметил проблему при выборе ячейки из кода в методе viewDidLoad
так:
1) обходной путь делал это в viewDidAppear:
, если кто-то не возражает против заметной задержки между представлением представления и выбором ячейки
2) второе решение работало для меня, но код выглядит немного хрупким, поскольку он основан на внутренней реализации UITableViewCell
3) добавление собственного разделителя, по-видимому, является самым гибким и лучшим на данный момент, но требует большего количества кодировок:)
Ответ 27
Настройка стиля в viewWillAppear
работала для меня.
Ответ 28
Поскольку это все еще проблема с IOS 8, я добавлю свое решение в Swift. Установите для разделителя разделителя таблицы значение 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()
}
Ответ 29
Вот мое решение этой проблемы. После того, как вы вставьте свою ячейку (ы), перезагрузите раздел. Если перезагрузка всего раздела слишком интенсивна для вас, просто перезагрузите 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];
Ответ 30
столкнулся с аналогичной проблемой, я обнаружил, что другое хорошее решение, особенно если ваш источник данных невелик, перезагружает tableData при реализации метода -(void)scrollViewDidScroll:(UIScrollView *)scrollView
, вот пример:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == self.tableView1) {
[self.tableView1 reloadData];
}
else {
[self.tableView2 reloadData];
}
}
Вы также можете просто перезагрузить невидимые данные на основе видимого источника данных, но для этого требуется еще немного взлома.
Имейте в виду, что эта функция делегата относится к протоколу UITableViewDelegate
!
Надеюсь, что это поможет!