„Automatyczny układ nadal wymagany po wykonaniu -layoutSubviews” z podklasą UITableViewCell

115

Używając XCode 4.5 i iOS 6, tworzę aplikację z prostym widokiem tabeli z niestandardowymi komórkami. Robiłem to sto razy w iOS 5 i niższych, ale z jakiegoś powodu nowy system autoLayout sprawia mi wiele problemów.

Skonfigurowałem widok tabeli i prototypową komórkę w IB, dodałem podglądy i podłączyłem je jako IBOutlets, a następnie skonfigurowałem delegata i źródło danych. Jednak teraz za każdym razem, gdy pobierana jest pierwsza komórka, pojawia cellForRowAtIndexPathsię następujący błąd:

*** Błąd potwierdzenia w - [ShopCell layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2372/UIView.m:5776

*** Zamykanie aplikacji z powodu nieprzechwyconego wyjątku „NSInternalInconsistencyException”, przyczyna: „Auto Layout nadal wymagane po wykonaniu -layoutSubviews. Wdrożenie -layoutSubviews przez ShopCell musi być super.

Nie zaimplementowałem metody -layoutSubviews w mojej komórce podklasowej (ShopCell), a nawet gdy próbuję to zrobić i dodać super wywołanie, jak sugeruje, nadal otrzymuję ten sam błąd. Jeśli usunę podglądy z komórki w IB i zmienię ją na standardowy UITableViewCell, wszystko działa zgodnie z oczekiwaniami, chociaż oczywiście nie mam żadnych danych w moich komórkach.

Jestem prawie pewien, że brakuje mi czegoś prostego, ale nie mogę znaleźć żadnej dokumentacji ani przewodników, które sugerowałyby, co zrobiłem źle. Każda pomoc będzie mile widziana.

Edycja: po prostu próbowałem zmienić go na UITableViewCell w IB i pozostawić wszystkie podglądy na miejscu, wciąż ten sam błąd.

Mike Mayo
źródło
Spróbuj lldb [[UIWindow keyWindow] _autoLayoutTrace]w obszarze debuggera, jeśli używany jest układ automatyczny.
A-Live
3
Czy używasz UIView dla niestandardowej komórki zamiast UITableViewCell? Miałem ten sam problem. Miałem UIView dla niestandardowej komórki i dodawałem do tego widoki podrzędne. Zmieniono na UITableViewCell i zadziałało.
Hej Mike, jak definiujesz punkty sprzedaży? Czy są to właściwości w pliku implementacji w rozszerzeniu klasy?
kocodude
@ A-Live Za każdym razem, gdy próbuję użyć tej metody, pojawia się błąd w debugerze… czy ta metoda jest nadal ważna? Edycja: nieważne, jest to mała litera l w automatycznym układzie.
borrrden
odznacz pole autoLayout w inspektorze, a następnie wyczyść i uruchom. to z pewnością zadziała.
Nico

Odpowiedzi:

57

Napotkałem ten sam problem podczas ręcznego dodawania ograniczeń w kodzie. W kodzie wykonałem następujące czynności:

{
    [self setTranslatesAutoresizingMaskIntoConstraints:YES];
    [self addSubview:someView];
    [self addSubview:someOtherView];
    [self addConstraint:...];
}

Hipoteza

Z tego, co mogę powiedzieć, problem polega na tym, że po wyłączeniu translatesAutoresizingMaskIntoConstraintsUITableViewCell zaczyna używać automatycznego układu i naturalnie kończy się niepowodzeniem, ponieważ podstawowa implementacja layoutSublayersForLayernie wywołuje super. Ktoś z Hopperem lub jakimś innym narzędziem może to potwierdzić. Ponieważ używasz IB, prawdopodobnie zastanawiasz się, dlaczego jest to problem ... a to dlatego, że użycie IB automatycznie wyłącza translatesAutoresizingMaskIntoConstraintsdla widoków, do których dodaje ograniczenia (automatycznie doda ograniczenie szerokości i wysokości w ich miejsce).

Rozwiązanie

Moim rozwiązaniem było przeniesienie wszystkiego do contentView.

{
   [self.contentView addSubview:someView];
   [self.contentView addSubview:someOtherView];
   [self.contentView addConstraint:...];
}

Nie jestem w 100% pewien, czy to zadziała w Interface Builder, ale jeśli wypchniesz wszystko z komórki (zakładając, że masz coś bezpośrednio na niej), to powinno działać. Mam nadzieję, że to ci pomoże!

Malaxeur
źródło
4
Musiałem również dodać subview.translatesAutoresizingMaskIntoConstraints = NO'do każdego widoku podrzędnego, który dodawałem do contentView.
Jay Peyer
5
To zadziałało dla mnie. Upewnij się również, że nie dzwonisz self.contentView.translatesAutoresizingMaskIntoConstraints = NOpo UITableViewCell.
Maurizio
53

Najwyraźniej implementacja layoutSubviews w UITableViewCell nie wywołuje super, co jest problemem z automatycznym układem. Byłbym zainteresowany, aby sprawdzić, czy upuszczenie poniższej kategorii do projektów rozwiązuje problem. Pomogło w projekcie testowym.

#import <objc/runtime.h>
#import <objc/message.h>

@implementation UITableViewCell (FixUITableViewCellAutolayoutIHope)

+ (void)load
{
    Method existing = class_getInstanceMethod(self, @selector(layoutSubviews));
    Method new = class_getInstanceMethod(self, @selector(_autolayout_replacementLayoutSubviews));

    method_exchangeImplementations(existing, new);
}

- (void)_autolayout_replacementLayoutSubviews
{
    [super layoutSubviews];
    [self _autolayout_replacementLayoutSubviews]; // not recursive due to method swizzling
    [super layoutSubviews];
}

@end

Mogę dodać problem, który pojawił się podczas korzystania z backgroundView w komórce tabeli, ponieważ jest on dodawany jako podwidok do komórki (podczas gdy większość podglądów podrzędnych należy dodać do contentView komórki tabeli, co zwykle powinno działać lepiej).

Uwaga: wygląda na to, że ten błąd został naprawiony w iOS7; Udało mi się usunąć ten kod lub przynajmniej dodać sprawdzenie środowiska uruchomieniowego, aby było to wykonywane tylko wtedy, gdy działa na iOS6.

Carl Lindberg
źródło
Dziwne jest to, że działa dobrze ze zwykłym UITableViewCell dla mnie, ale nie dla podklasy ...
borrrden
Można by pomyśleć, ale wszystko co mam to metoda init, nic więcej> <. Nigdy w życiu nie przesłoniłem layoutów, haha. Myślę, że problemem jest niestandardowy widok, którego UITableViewCell używa jako widoku głównego, nie może używać automatycznego układu, ponieważ zastępuje layoutSubviews (więc kiedy spróbujesz dodać ograniczenia do widoku głównego, nie powiedzie się)
borrrden
6
Musiałem stworzyć taką kategorię z UITableViewtego samego powodu (iOS 6.1 b1)
Joshua J. McKinnon
5
Czy istnieje podobna poprawka dla TableHeaderView, ponieważ problem nadal występuje w systemie iOS 7?
Softlion
1
To działa świetnie. Napotkałem ten problem, gdy próbowałem wyśrodkować podwidok UIVIew w UITableView. Nawet w iOS 7 asercja występuje. Ale nie występuje w iOS 8, więc musieli rozwiązać ten błąd.
Jordan H,
33

Miałem ten sam błąd przez kilka miesięcy. Ale znalazłem, w czym problem.

Kiedy tworzę plik IB, UIViewjest już dodany. Jeśli używasz tego widoku, aplikacja nie ulega awarii, gdy układ automatyczny jest wyłączony (ale są też inne problemy). Podczas korzystania z układu auto, trzeba wybrać właściwą widok w bibliotece obiektów: UITableViewCell.

W rzeczywistości, należy zawsze używać tego elementu, ponieważ wszystkie subviews są dodawane do contentViewz UITableViewCell.

To wszystko. Wszystko będzie dobrze.

Arnaud
źródło
Nie powinna to być akceptowana odpowiedź, ponieważ pytanie nie dotyczy implementacji przy użyciu IB i ponieważ ten problem może się zdarzyć, gdy nie używasz IB. Jeśli robisz swoje widoki programowo, odpowiedź @ PhilLoden jest bardziej realna.
Eric
Nie rozumiem odpowiedzi. czy ktoś może wyjaśnić jaśniej? dzięki
hasan
Myślę, że mam do tego prawo. czy wystarczy sprawdzić klasę att. w inspektorze tożsamości w konstruktorze interfejsów? albo to, co zostało dodane, było innego typu i klasy. został zaktualizowany później? czy to również powoduje problem?
hasan
@ hasan83 Możesz faktycznie zwrócić komórkę. UITableViewCell to w zasadzie UIView z identyfikatorem ponownego użycia.
Arnaud
17

Miałem ten sam problem z niestandardowym UITableViewHeaderFooterView+ XIB.

Widziałem tutaj kilka odpowiedzi, ale znalazłem implementację -layoutSubviewsw mojej niestandardowej klasie widoku stopki rozwiązuje problem:

-(void)layoutSubviews
{
    [super layoutSubviews];
    [self layoutIfNeeded]; // this line is key
}
Blaster dźwięku
źródło
1
Uważaj, może to spowodować nieskończoną pętlę i ostatecznie EXC_BAD_ACCESS KERN_PROTECTION_FAILURE
mbi
15

Widziałem to jako wynik modyfikacji ograniczeń w mojej implementacji layoutSubviews. Przeniesienie wywołania na super od początku do końca metody rozwiązało problem.

Phil Loden
źródło
To zadziałało dla mnie. Mam niestandardową UICollectionViewCell, którą formatuję w layoutSubviews. Czy ktoś wie, dlaczego to rozwiązanie działa?
STANGMMX
@STANGMMX, odpowiedź A'sa Dickensa wyjaśnia dlaczego.
Fábio Oliveira
15

Miałem ten sam problem w iOS 7 (wydaje się, że iOS 8 rozwiązuje). Rozwiązaniem dla mnie było wywołanie [self.view layoutIfNeeded]na końcu mojej viewDidLayoutSubviewsmetody.

Keller
źródło
Dziękuję Ci. Pomaga mi, spotkałem się z tym problemem wczoraj (na iOS 7). to pomaga dla iOS 7.
Alexander
@MaciejSwic zobacz moją odpowiedź na górze.
Sound Blaster
To zadziałało dla mnie! Korzystanie z iOS 7.1 ze Swift. Usuwałem i dodawałem ograniczenie na viewDidLayoutSubviews. Usunąłem super wywołanie i nadal nie działało, ale to rozwiązanie załatwiło sprawę! daj temu dinozaurowi liść! :)
jomafer
U mnie też działało na iOS 7.1!
fdlr
14

Miałem ten sam problem. Problem tkwił w sposobie tworzenia komórki Xib. Utworzyłem Xib jak zwykle i właśnie zmieniłem typ domyślnego „UIView” na moją niestandardową klasę UITableViewCell. Prawidłowym sposobem jest najpierw usunięcie domyślnego widoku, a następnie przeciągnięcie obiektu komórki widoku tabeli do xib. Więcej szczegółów tutaj: http://allplayers.github.io/blog/2012/11/18/Custom-UITableViewCell-With-NIB/

Trunal Bhanse
źródło
1
Doskonały wgląd! Zajęłoby mi to całe wieki, zanim zdałem sobie z tego sprawę, zwłaszcza że moje aplikacje nie uległyby awarii, gdybym używał UIView z wyłączoną funkcją AutoLayout.
Guilherme
Wspaniały! zobacz także odpowiedź @Arnaud poniżej
Lubbo
7

Rozwiązałem ten problem, wyłączając opcję „Automatyczne układanie” dla wszystkich widoków podrzędnych mojej niestandardowej komórki widoku tabeli.

W xib dla komórki niestandardowej wybierz widok podrzędny i odznacz opcję Inspektor plików> Dokument konstruktora interfejsu> Użyj automatycznego układu

Johno
źródło
4
Zrobiłem to samo. Naprawdę nie jest rozwiązaniem, choć jeśli chcesz używać autoLayout choć
ajmccall
7

Miałem podobny problem nie na sobie, UITableViewCellale raczej na UITableViewsobie. Ponieważ jest to pierwszy wynik w Google, opublikuję go tutaj. Okazało się, że to viewForHeaderInSectionbył problem. I stworzył UITableViewHeaderFooterViewi zestaw translatesAutoresizingMaskIntoConstraintsdo NO. A teraz interesująca część:

iOS 7:

// don't do this on iOS 7
sectionHeader.translatesAutoresizingMaskIntoConstraints = NO;

Jeśli to zrobię, aplikacja zawiesza się z

Układ automatyczny nadal wymagany po wykonaniu -layoutSubviews. Implementacja -layoutSubviews w UITableView musi wywołać super.

OK, pomyślałem, że nie możesz użyć automatycznego układu w nagłówku widoku tabeli i tylko w podglądzie. Ale to nie jest cała prawda, jak zobaczysz później. Podsumowując: nie wyłączaj maski automatycznej zmiany rozmiaru dla nagłówka w iOS 7. W przeciwnym razie działa dobrze.

iOS 8:

// you have to do this, otherwise you get an auto layout error
sectionHeader.translatesAutoresizingMaskIntoConstraints = NO;

Gdybym tego nie użył, otrzymałbym następujący wynik:

Nie można jednocześnie spełnić ograniczeń.

W przypadku iOS 8 musisz wyłączyć automatyczną zmianę rozmiaru maski dla nagłówka.

Nie wiem, dlaczego zachowuje się w ten sposób, ale wydaje się, że Apple naprawił niektóre rzeczy w iOS 8, a układ automatyczny działa inaczej na iOS 7 i iOS 8.

testowanie
źródło
Stary, po prostu ocal mój dzień!
Marcin Małysz
5

Jak ktoś powyżej już powiedział, kiedy tworzysz widok do użycia w UITableView, musisz usunąć widok utworzony domyślnie i przeciągnąć UITableViewCell lub UITableViewHeaderFooterView jako widok główny. Istnieje jednak sposób na naprawienie XIB na wypadek, gdybyś przegapił tę część. Musisz otworzyć plik XIB w edytorze tekstowym, a w tagu korzeniowego i jego bezpośredni child Dodaj / zmienić atrybut translatesAutoresizingMaskIntoConstraintsdo YES, na przykład

<view contentMode="scaleToFill" horizontalHuggingPriority="1000" id="1" translatesAutoresizingMaskIntoConstraints="YES" customClass="MXWCollapsibleTableHeaderView">

Maksymilian Wojakowski
źródło
2

Spotykam się z tym i wydaje się, że jest to związane z podklasami UITableViewCell jako komórkami prototypowymi, które specjalnie mają dodane do nich inne niestandardowe podklasy UIView. Podkreślam tutaj `` zwyczaj '', ponieważ odniosłem sukces z komórkami, które mają tylko dzieci UIKit, ale przewraca się, gdy próbuję zbudować ograniczenia dla widoków, które stworzyłem na zamówienie, rzucając błąd podany w pytaniu autorów.

Musiałem podzielić moje komórki na niezależne końcówki, które nie używają AutoLayout.

Miejmy nadzieję, że Apple posprząta ten bałagan.

Lee Probert
źródło
2

Dodaj widoki podrzędne do contentView komórki zamiast samej komórki. Więc zamiast:

[self addSubview:someView];

musisz użyć

[self.contentView addSubview:someView];

Christian Aberger
źródło
1

Napotkałem ten, ponieważ początkowo dodałem UIView zamiast UITableViewCell do pliku xib.

mjmdavis
źródło
1

Wyeliminowałem ten błąd, odłączając backgroundViewzłącze od mojego tła UIImageViewi accessoryViewzłącze od moich UIButtondostosowań. Podejrzewam, że nie miały być używane w taki sposób, w jaki ich używałem.

Owen Godfrey
źródło
1

Dziś pierwszy raz zetknąłem się z tym problemem. Do tej pory miałem różne doświadczenia w używaniu prototypowych podklas UITableViewCell, ale nigdy nie napotkałem tego problemu. Różnica w komórce, z którą pracowałem, polegała na tym, że miałem IBOutlet do -backgroundView, którego użyłem do pokolorowania komórki. Okazało się, że jeśli utworzę nową właściwość i nadal dodam nowy UIView, który rozciągnąłby rozpiętość całej komórki, to stwierdzenie zniknęło. Aby sprawdzić, czy to była przyczyna, wróciłem do dołączania tego widoku do gniazda backgroundView i stwierdzenie pojawiło się ponownie. Jak dotąd nie ma innych problemów z używaniem AutoLayout w podklasie prototypu UITableViewCell od czasu wprowadzenia tej zmiany.

Eric Schramm
źródło
1

Nie otrzymałem żadnego odpowiedniego rozwiązania tego problemu, ale możesz to naprawić, używając ramek i nie ustawiając właściwości translatesAutoresizingMaskIntoConstraints na Nie (domyślnie tak, więc nie ustawiaj jej)

CGRect headerViewFrame = CGRectMake(0,0,320,60); //Frame for your header/footer view
UIView *tableHeaderView = [[UIView alloc] initWithFrame:headerViewFrame];
tableHeaderView.translatesAutoresizingMaskIntoConstraints = Yes; //Or don't set it at all
[self.tableView setTableHeaderView:tableHeaderView];
sanjana
źródło
0

Doświadczałem tego samego. Okazało się, że jeśli programowo dodasz podwidok ze swojego ShopCell .xib / storyboard, który używa automatycznego układu, jako podwidok do innego widoku, ten wyjątek może zostać wyrzucony, w zależności od konfiguracji twoich ograniczeń. Domyślam się, że ograniczenia utworzone w IB są tym, co stwarza problemy podczas programowego dodawania widoku jako podglądu podrzędnego, ponieważ wtedy zachowuje ograniczenia z widoku A -> widokB, tymczasem możesz dodać widok B jako podwidok widoku C. Zrozumieliście (to zdanie jest nawet mylące)?

W mojej sytuacji - ponieważ przyczyną problemu były bardzo proste widoki - stworzyłem widoki programowo, a nie w IB. To rozwiązało problem. Możesz wyodrębnić te widoki do innych plików xib i wyłączyć dla nich automatyczny układ. Myślę, że to zadziała.

Kasper Munck
źródło
0

W niektórych sytuacjach rozwiązuje to łatwo problem z układem (w zależności od układu). W podklasie UITableView, w awakeFromNib lub init, ustaw maskę autoresize:

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

Domyślnie jest ustawiony na UIViewAutoresizingNone

Arie Litovsky
źródło
To rozwiązało problem, z którym miałem do czynienia. Używam automatycznego układu w komórce tabeli w połączeniu z, [tableViewCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].heightaby uzyskać wysokość, którą następnie używam heightForRowAtIndexPath.
NathanAldenSr
0

W moim przypadku,

Przywoływany UIImageView dla układu automatycznego dla UITableView jest przypisany do backgroundView UITableView.

self.tableView.backgroundView = self.tableBackgroundImageView;

Dlatego usunąłem UIImageView dla backgroundView z UIView (widok główny) i zresetowałem (usunąłem) wszystkie odwołania do układu automatycznego do tego UIImageView. Umieściłem ten UIImageView jako tło na zewnątrz z UIView (widok główny). A następnie przypisz do backgroundView UITableView w kodzie.

Następnie naprawiono.

Conan Kim
źródło
0

Znalazłem rozwiązanie.

W moim przypadku utworzyłem widok komórki w scenorysie (z włączonym automatycznym układem) i zdefiniowałem niestandardowy interfejs UITableViewCell w moim ViewController.m, muszę przenieść interfejs do ViewController.h.

Nim Thitipariwat
źródło
0

Napotkałem ten sam problem, gdy używam storyboardu do tworzenia niestandardowego UITableViewCell. Na szczęście znalazłem problem, ponieważ wyprowadziłem accessoryView ([UITableViewCell setAccessoryView:]) do UIButton, który dodałem do komórki.

Tak stało się w moim projekcie, gdy działałem na iOS6.

Rozwiązanie

Zwalniam gniazdo między akcesoriami accessoryView i moim przyciskiem, który zawierał niestandardową komórkę.

Wniosek

Nie należy używać natywnych elementów UITableViewCell i zmieniać ich.

Wanqiang Ji
źródło
0

Ten problem może być spowodowany zapominaniem o dzwonieniu do [super viewDidAppear:]wewnątrz viewDidAppear, ale jestem pewien, że nie jest to jedyna przyczyna.

michaelsnowden
źródło
0

Miałem dokładnie ten sam problem. Oto problem z moim projektem:
kiedy pracowałem nad Interface Builder, aby utworzyć niestandardowy UITableViewCell, przeciągnąłem Widok zamiast komórki widoku tabeli z okienka kolekcji obiektów w Xcode
jako niestandardową komórkę tabeli.
Jeśli jesteś w tej samej sytuacji, oto rozwiązanie:
Usuń widok w konstruktorze interfejsu, upewnij się, że przeciągasz komórkę widoku tabeli z okienka kolekcji obiektów i ponownie wykonaj niestandardowy widok komórki tabeli. Możesz skopiować obiekty ze starego widoku i wkleić je na kanwie nowej komórki widoku tabeli.

us_david
źródło
0

Miałem bardzo podobny problem z widokiem stopki tabeli, który ustawiałem w Xcode 6, iOS 7+. Rozwiązanie było w formacie pliku nib. Najwyraźniej utknął w formacie Xcode 4 czy coś w tym stylu. Zmiana ustawień pliku na „otwiera się w: Xcode 6.0” (lub Domyślnie, jeśli o to chodzi), natychmiast go naprawiła. Znalazłem rozwiązanie przypadkiem: doprowadzało mnie to do szału, więc usunąłem cały plik i utworzyłem ponownie, oczywiście z domyślnymi ustawieniami. Nie mam pojęcia, dlaczego zwykła edycja pliku w najnowszym Xcode nie konwertowała go do formatu Xcode 5+, jak to zwykle bywa.

fa

user3099609
źródło
0

Poszedłem miał ten sam problem. Poszedłem do mojego DetailViewController i zmieniłem nazwę identyfikatora na UIView. To było wcześniej w UITableView. To rozwiązało problem. Ten problem nie musi znajdować się w Twoim DetailViewController. Może być w każdym innym. Spróbuj zmienić jego nazwę na szanowany identyfikator.

RandomDude
źródło
0

Miałem podobny problem ze statycznymi komórkami widoku tabeli w IB. Jedna z komórek miała podwidok, który miał klasę, która została omyłkowo zmieniona na podklasę UITextfield. Kompilator nie dał żadnych ostrzeżeń / błędów. Jednak w czasie wykonywania system nie był w stanie załadować kontrolera widoku z powodu wyżej wymienionej awarii.

Yannick Winters
źródło
0

Problem polega na sekwencjonowaniu wywołań układu do widoków podrzędnych:

Sprawdzić

Pojawia się w iOS <8

Shanu ji
źródło
0

Rozwiązanie: zmień ograniczenia przed wywołaniem super layoutSubviews

- (void)layoutSubviews
{

    [self _updateConstraints];

    [super layoutSubviews];
}
abuharsky
źródło
0

Zmodyfikowałem odpowiedź Carla Lindberga, aby UITableViewzamiast tego zastąpić, i zaczęło działać dla mnie:

UITableView + AutoLayoutFix.h

@interface UITableView (AutoLayoutFix)
@end

UITableView + AutoLayoutFix.m

#import <objc/runtime.h>

@implementation UITableView (AutoLayoutFix)

+ (void)load
{
    Method existingMethod = class_getInstanceMethod(self, @selector(layoutSubviews));
    Method newMethod = class_getInstanceMethod(self, @selector(_autolayout_replacementLayoutSubviews));

    method_exchangeImplementations(existingMethod, newMethod);
}

- (void)_autolayout_replacementLayoutSubviews
{
    [super layoutSubviews];
    [self _autolayout_replacementLayoutSubviews]; // not recursive due to method swizzling
    [super layoutSubviews];
}

@end

Następnie MyViewController.mwłaśnie zaimportowałem kategorię:

#import "UITableView+AutoLayoutFix.h"
oracz
źródło
0

Spotkałem ten sam problem i ostatecznie znalazłem powód , dla którego dodałem jedno ograniczenie do UITableViewCell, które powinno być contentView UITableViewCell . Kiedy zmieniłem ograniczenie, wszystko poszło dobrze!

Alfy
źródło