Mam dziwny problem UITableView
tylko z iOS 7.
UITableViewCellSeparator
znika nad pierwszym i pod ostatnim wierszem. Czasami pojawia się po wybraniu wierszy lub przewijaniu.
W moim przypadku tableView
ładuje się Storyboard
ze UITableViewStylePlain
stylu. Problem na pewno nie występuje UITableViewCellSeparatorStyle
, co nie jest zmieniane z domyślnego UITableViewCellSeparatorStyleSingleLine
.
Jak czytałem na forach deweloperów Apple ( tutaj i tutaj ), inne osoby mają taki problem i znaleziono kilka obejść, na przykład:
Workaround: disable the default selection and recreate the behaviour in a method
trigged by a tapGestureRecognizer.
Ale wciąż szukam przyczyny takiego dziwnego zachowania separatora.
Jakieś pomysły?
Aktualizacja: Jak widziałem w XCode 5.1 DP i iOS 7.1 beta, Apple próbował naprawić ten problem. Teraz separator jest wyświetlany w razie potrzeby, czasami poniżej ostatniego wiersza, po pewnym odświeżeniu, ale nie po utworzeniu widoku tabeli.
Odpowiedzi:
Zrzuciłem hierarchię widoku podrzędnego komórek, których dotyczy problem, i stwierdziłem, że
_UITableViewCellSeparatorView
została ustawiona jako ukryta. Nic dziwnego, że go nie pokazano!Zastąpiłem
layoutSubviews
w mojejUITableViewCell
podklasie i teraz separatory są wyświetlane niezawodnie:Cel-C :
- (void)layoutSubviews { [super layoutSubviews]; for (UIView *subview in self.contentView.superview.subviews) { if ([NSStringFromClass(subview.class) hasSuffix:@"SeparatorView"]) { subview.hidden = NO; } } }
Szybki :
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 } } }
Inne zaproponowane tutaj rozwiązania nie działały u mnie konsekwentnie lub wydają się niezgrabne (dodawanie niestandardowych widoków stopki 1 px).
źródło
To zadziałało dla mnie:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // fix for separators bug in iOS 7 self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
źródło
Miałem też problem z brakującymi separator i okazało się, że jedyny problem wystąpił, gdy
heightForRowAtIndexPath
został powrocie liczbę dziesiętną . Rozwiązanie:override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { return ceil(yourHeight) // Ceiling this value fixes disappearing separators }
źródło
Czy próbowałeś dodać UIView wysokości 1 w nagłówku i stopce tabeli z jasnoszarym tłem? Zasadniczo będzie kpić z pierwszego i ostatniego separatora.
źródło
@samvermette
Rozwiązałem problem, używając tych metod delegata. Teraz nie migocze:
-(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; }
źródło
Napotkaliśmy ten problem w naszej aplikacji. Gdy użytkownik wybrał komórkę, nowy widok tabeli został wypchnięty na stos kontrolera nawigacji, a gdy użytkownik go zdjął, brakowało separatora. Rozwiązaliśmy go poprzez umieszczenie
[self.tableView deselectRowAtIndexPath:indexPath animated:NO];
wdidSelectRowAtIndexPath
widoku tabeli metody delegata.źródło
self.tableView.allowsMultipleSelection = NO;
?Oto łatwiejsze (choć nieco niechlujne) obejście, jeśli potrzebujesz, spróbuj zaznaczyć i odznaczyć komórkę po ponownym załadowaniu danych i zakończeniu domyślnej animacji.
poprostu dodaj:
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; [self.tableView deselectRowAtIndexPath:indexPath animated:NO];
źródło
Odkryłem, że najłatwiejszym rozwiązaniem jest, po przeładowaniu komórki, również przeładowanie komórki powyżej:
if (indexPath.row > 0) { NSIndexPath *path = [NSIndexPath indexPathForRow:indexPath.row - 1 inSection:indexPath.section]; [self.tableView reloadRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationNone]; }
źródło
Proste i czyste rozwiązanie, które działa zarówno na iOS 8, jak i iOS 9 (beta 1)
Oto proste, przejrzyste i nieinwazyjne obejście. Polega na wywołaniu metody kategorii, która naprawi separatory.
Wszystko, co musisz zrobić, to zejść w dół hierarchii komórki i odkryć separator. Lubię to:
for (UIView *subview in cell.contentView.superview.subviews) { if ([NSStringFromClass(subview.class) hasSuffix:@"SeparatorView"]) { subview.hidden = NO; } }
Polecam dodanie tego do kategorii na UITableViewCell, na przykład:
@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
Ponieważ separator może zniknąć w innej komórce niż obecnie zaznaczona, prawdopodobnie dobrym pomysłem jest wywołanie tej poprawki we wszystkich komórkach w widoku tabeli. W tym celu możesz dodać kategorię do UITableView, która wygląda następująco:
@implementation UITableView (fixSeparators) - (void)fixSeparators { for (UITableViewCell *cell in self.visibleCells) { [cell fixSeparator]; } } @end
Mając to na miejscu, możesz wywołać
-fixSeparatos
swój tableView zaraz po akcji, która powoduje ich zniknięcie. W moim przypadku było to po zadzwonieniu[tableView beginUpdates]
i[tableView endUpdates]
.Jak wspomniałem na początku, przetestowałem to zarówno na iOS 8, jak i iOS 9. Zakładam, że będzie działać nawet na iOS 7, ale nie mam sposobu, aby to wypróbować. Jak zapewne wiesz, ma to wpływ na wnętrze komórki, więc może przestać działać w jakiejś przyszłej wersji. I Apple mógłby teoretycznie (0,001% szans) odrzucić twoją aplikację z tego powodu, ale nie widzę, jak mogliby nawet dowiedzieć się, co tam robisz (sprawdzenie sufiksu klasy nie może zostać wykryte przez analizatory statyczne jako coś źle, IMO).
źródło
Opierając się na komentarzu @ ortwin-gentz, to rozwiązanie działa u mnie w 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 } } }
(Kod SWIFT)
Używam funkcji fixCellsSeparator () po wywołaniu endUpdates () w niektórych metodach mojego tableView, na przykład:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { // Perform some stuff // ... self.tableView.endUpdates() self.fixCellsSeparator() }
Mam nadzieję, że to rozwiązanie okaże się komuś pomocne!
źródło
Uzupełnienie odpowiedzi airpaulg.
Zasadniczo należy zaimplementować dwie metody UITableDelegate. Oto moje rozwiązanie, które działa zarówno na iOS7, jak i 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]
// Spowoduje to ukrycie pustej siatki tabeli pod komórkami, jeśli nie zajmują one całego ekranu
- (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; } }
źródło
To rozwiązało problem:
Upewnij się, że
clipsToBounds
jest ustawiony na TAK dla komórki, ale NIE dla komórkicontentView
. Ustaw równieżcell.contentView.backgroundColor = [UIColor clearColor];
źródło
Wydaje się, że ten problem objawia się w tak wielu okolicznościach.
Dla mnie miało to coś wspólnego z selekcją komórek. Nie mam pojęcia dlaczego i nie zdążyłem wkopać się w to zbyt głęboko, ale mogę powiedzieć, że zaczęło się to dziać, kiedy ustawiłem komórkę
selectionStyle
brak. to znaczy://This line brought up the issue for me cell.selectionStyle = UITableViewCellSelectionStyleNone;
Próbowałem użyć niektórych metod delegata powyżej, które włączają i wyłączają
separatorStyle
właściwość,tableView
ale wydawało się, że nie zrobiły nic, aby rozwiązać mój problem.Jedynym powodem, dla którego tego potrzebowałem, było to, że nie potrzebowałem nawet selekcji komórek.
Więc znalazłem coś, co dla mnie zadziałało. Właśnie wyłączyłem wybór na
UITableView
:tableView.allowsSelection = NO;
Mam nadzieję, że to komuś pomoże, jeśli nie musisz wybierać komórek.
źródło
Próbowałem tak wielu sugestii, ale nie mogę tego naprawić, ostatecznie zdecydowałem się na niestandardową linię separatora, jak poniżej:
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: custom at willDisplayCell NOT cellForRowAtIndexPath
źródło
Dość zaskakujące
separatorInset
wydaje się , że zmiana wartości komórki w tę iz powrotem działa: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;
źródło
Miałem również problem z tym niespójnym wyświetlaniem linii separatora na dole mojego UITableView. Korzystając z niestandardowego widoku stopki, mogłem utworzyć podobną linię i wyświetlić ją na potencjalnej istniejącej (której czasami brakowało).
Jedynym problemem tego rozwiązania jest to, że Apple może kiedyś zmienić grubość żyłki
- (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; }
źródło
Rozwiązałem kwestię umieszczenia tych wierszy kodu w miejscu, w którym następuje aktualizacja widoku tabeli:
self.tableView.separatorStyle = UITableViewCellSeparatorStyle.None; self.tableView.separatorStyle = UITableViewCellSeparatorStyle.SingleLine;
Na przykład w moim przypadku umieściłem je tutaj:
tableView.beginUpdates() tableView.insertRowsAtIndexPaths(insertIndexPaths, withRowAnimation: UITableViewRowAnimation.Fade) tableView.endUpdates() self.tableView.separatorStyle = UITableViewCellSeparatorStyle.None; self.tableView.separatorStyle = UITableViewCellSeparatorStyle.SingleLine;
źródło
Musisz usunąć zaznaczenie komórek przed wykonaniem aktualizacji komórki. Następnie możesz przywrócić zaznaczenie.
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];
źródło
Aby rozwiązać powyższy problem, dodaj pustą stopkę w viewDidLoad.
UIView *emptyView_ = [[UIView alloc] initWithFrame:CGRectZero]; emptyView_.backgroundColor = [UIColor clearColor]; [tableView setTableFooterView:emptyView_];
Nie używaj powyższych wierszy w metodzie delegata viewForFooterInSection. Oznacza Nie implementuj metody viewForFooterInSection.
źródło
aby rozwinąć odpowiedź airpaulg, ponieważ nie działała ona całkowicie dla mnie.
Musiałem również wdrożyć metodę heightforfooter, aby uzyskać wysokość
potem zauważyłem, że jasny kolor jest zbyt ciemny. Naprawiłem to, chwytając bieżący kolor separatora w widoku tabeli i używając tego:
UIColor *separatorGray = [self.briefcaseTableView separatorColor]; [footerSeparator setBackgroundColor:separatorGray];
źródło
Również w naszym projekcie napotkałem ten problem. Mój widok tabeli miał tableFooterView, który mógł to zrobić. Znalazłem separator poniżej ostatniego wiersza, który pojawiłby się, gdybym usunął ten tableFooterView.
źródło
To, co zrobiło dla mnie różnicę, to ponowne załadowanie wiersza, dla którego dolna linia separatora się nie pojawiła:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]; [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
źródło
Rozwiązuję ten problem w inny sposób: dodaj warstwę o wysokości 0,5 piksela i jasnoszarym kolorze do tableview.tableFooterView jako podwarstwy.
kod wygląda tak:
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;
źródło
W swojej podklasie UITableViewCell zaimplementuj layoutSubviews i dodaj:
- (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; } } }
źródło
Jak ktoś inny wspomniał, problem ten wydaje się objawiać na różne sposoby. Rozwiązałem swój problem, stosując następujące obejście:
[tableView beginUpdates]; [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; [tableView endUpdates];
źródło
Przeglądając odpowiedzi i rozwiązania doszedłem do takich spostrzeżeń:
Wydaje się to być problematyczne zarówno w iOS 7, jak i 8. Zauważyłem problem podczas wybierania komórki z kodu w
viewDidLoad
metodzie, więc:1) obejście miało miejsce,
viewDidAppear:
gdy komuś nie przeszkadzało zauważalne opóźnienie między prezentacją widoku a wybraniem komórki2) drugie rozwiązanie działało dla mnie, ale kod wygląda na nieco kruchy, ponieważ opiera się na wewnętrznej implementacji
UITableViewCell
3) dodanie własnego separatora wydaje się na razie najbardziej elastyczne i najlepsze, ale wymaga więcej kodowania :)
źródło
U
viewWillAppear
mnie zadziałało ustawienie stylu .źródło
Ponieważ jest to nadal problem z IOS 8, dodam moje rozwiązanie w Swift. Ustaw linię separatora widoku tabeli na none. Następnie dodaj ten kod do metody delegata cellForRowAtIndexPath. Doda ładny separator. Instrukcja if pozwala zdecydować, które komórki powinny mieć separator.
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() }
źródło
Oto moje rozwiązanie tego problemu. Po wstawieniu komórek ponownie załaduj sekcję. Jeśli przeładowanie całej sekcji jest dla Ciebie zbyt intensywne, po prostu przeładuj indexPaths powyżej i poniżej.
[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];
źródło
natknąłem się na podobny problem, stwierdziłem, że innym dobrym rozwiązaniem, zwłaszcza jeśli twoje źródło danych nie jest duże, jest ponowne załadowanie tableData podczas implementacji
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
metody, oto przykład:-(void)scrollViewDidScroll:(UIScrollView *)scrollView { if (scrollView == self.tableView1) { [self.tableView1 reloadData]; } else { [self.tableView2 reloadData]; } }
Możesz także po prostu przeładować niewidoczne dane na podstawie widocznego źródła danych, ale wymaga to więcej hakowania.
Należy pamiętać, że ta funkcja delegata należy do
UITableViewDelegate
protokołowi!Mam nadzieję, że to pomoże!
źródło