Problem z ograniczeniami układu automatycznego w systemie iOS7 w UITableViewCell

104

Programowo używam ograniczeń układu automatycznego do układania moich niestandardowych komórek UITableView i poprawnie definiuję rozmiary komórek w tableView:heightForRowAtIndexPath:

Działa dobrze na iOS6 i wygląda dobrze również w iOS7

ALE kiedy uruchamiam aplikację na iOS7, oto rodzaj komunikatu, który widzę w konsoli:

Break on objc_exception_throw to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2013-10-02 09:56:44.847 Vente-Exclusive[76306:a0b] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
        "<NSLayoutConstraint:0xac4c5f0 V:|-(15)-[UIImageView:0xac47f50]   (Names: '|':UITableViewCellContentView:0xd93e850 )>",
        "<NSLayoutConstraint:0xac43620 V:[UIImageView:0xac47f50(62)]>",
        "<NSLayoutConstraint:0xac43650 V:[UIImageView:0xac47f50]-(>=0)-[UIView:0xac4d0f0]>",
        "<NSLayoutConstraint:0xac43680 V:[UIView:0xac4d0f0(1)]>",
        "<NSLayoutConstraint:0xac436b0 V:[UIView:0xac4d0f0]-(0)-|   (Names: '|':UITableViewCellContentView:0xd93e850 )>",
        "<NSAutoresizingMaskLayoutConstraint:0xac6b120 h=--& v=--& V:[UITableViewCellContentView:0xd93e850(44)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0xac43650 V:[UIImageView:0xac47f50]-(>=0)-[UIView:0xac4d0f0]>

I rzeczywiście jest jedno z ograniczeń na tej liście, których nie chcę:

"<NSAutoresizingMaskLayoutConstraint:0xac6b120 h=--& v=--& V:[UITableViewCellContentView:0xd93e850(44)]>"

i nie mogę ustawić translatesAutoresizingMaskIntoConstraintswłaściwości contentViewna NIE => zepsułoby to całą komórkę.

44 jest domyślną wysokością komórki, ale zdefiniowałem moje niestandardowe wysokości w delegacie widoku tabeli, więc dlaczego komórka contentView ma to ograniczenie? Co może to spowodować?

W iOS6 tak się nie dzieje i wszystko wygląda dobrze zarówno na iOS6, jak i iOS7.

Mój kod jest dość duży, więc nie opublikuję go tutaj, ale możesz poprosić o pastebin, jeśli go potrzebujesz.

Aby określić, jak to robię, podczas inicjalizacji komórki:

  • Tworzę wszystkie swoje etykiety, przyciski itp
  • Ustawiłem ich translatesAutoresizingMaskIntoConstraintswłasność na NIE
  • Dodam je jako podglądy contentViewkomórki
  • Dodaję ograniczenia do contentView

Jestem też głęboko zainteresowany zrozumieniem, dlaczego dzieje się tak tylko na iOS7.

Alexis
źródło
Jaka jest Twoja niestandardowa wysokość komórki?
Mike Pollard
moja niestandardowa wysokość komórki jest ustawiona na 90
Alexis
Kolega miał wczoraj ten sam problem, ale z domyślną szerokością 320 (dla aplikacji na iPada).
jrturton
Jest tutaj przykładowy projekt demonstrujący ten sam problem: github.com/Alex311/TableCellWithAutoLayout Oto kilka moich obserwacji na ten temat: github.com/Alex311/TableCellWithAutoLayout/commit/ ...
smileyborg
Napotkałem ten sam błąd, ale wynikał on z tego, że nigdy nie zainicjowałem dodatkowej prototypowej komórki, której użyłem do znalezienia wysokości mojej niestandardowej komórki.
Steve Moser

Odpowiedzi:

132

Miałem również ten problem. Wygląda na to, że ramka contentView nie jest aktualizowana, dopóki nie layoutSubviewszostanie wywołana, jednak ramka komórki jest aktualizowana wcześniej, pozostawiając ramkę contentView ustawioną na {0, 0, 320, 44}w momencie oceny ograniczeń.

Po dokładniejszym przyjrzeniu się contentView wydaje się, że autoresizeMasks nie są już ustawiane.

Ustawienie autoresizingMask przed ograniczeniem widoków może rozwiązać ten problem:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
    if (self)
    {
        self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
        [self loadViews];
        [self constrainViews];
    }
    return self;
}
liamnichole
źródło
To działa dzięki człowiekowi. Co może się stać, gdy używasz stylów edycji?
Alexis
11
@ L14M333 Zobacz kod, który zamieściłem w tym komentarzu tutaj - w zasadzie powinieneś być w stanie ustawić self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);przed dodaniem ograniczeń, co powinno rozwiązać problem.
smileyborg
2
autoresizingMask nie działa dla mnie: początkowe ograniczenie zniknęło, ale 3 nowe zostały dodane "<NSAutoresizingMaskLayoutConstraint: 0x8b79740 h = - & - v = - & - UITableViewCellContentView: 0x8b44010.height == UITableViewCellScrollView: 0x8b4a130, "<NSAutoresizingMaskLayoutConstraint: 0x8b7b9f0 h = - & - v = - & - UITableViewCellScrollView: 0x8b4a130.height == LibraryCell: 0x8ac19d0.height>", "<NSAutoresizingMaskLayoutConstraint: 0x8b4a130.height == LibraryCell: 0x8ac19d0.height>", "<NSAutoresizingMaskLayoutConstraint: 0x8b7: & V590: 0x8ac19d0 (0)]> "
catamphetamine
1
U mnie też działa, walczę z tym przez jakąś godzinę! Dzięki! <3
mokagio
2
Po pozbyciu się innych problemów z automatycznym układem (iOS 7 / iOS 8) dowiedziałem się, że używanie self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);działa równie dobrze jak używanie self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;. Umieściłem to updateConstraintsprzed dodaniem ograniczeń do widoku zawartości.
testowanie
35

Najwyraźniej coś jest nie tak z UITableViewCell i UICollectionViewCell na iOS 7 przy użyciu zestawu SDK iOS 8.

Możesz zaktualizować contentView komórki, gdy komórka jest ponownie używana w następujący sposób:

Dla statycznego UITableViewController:

#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];

    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1)
    {
        cell.contentView.frame = cell.bounds;
        cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    }

    //your code goes here

    return cell;
}

#endif
#endif

Ponieważ statyczne kontrolery widoku tabeli są kruche i można je łatwo zepsuć, jeśli zaimplementujesz niektóre źródła danych lub metody usunięcia danych - istnieją kontrole, które zapewnią, że ten kod zostanie skompilowany i uruchomiony tylko w systemie iOS 7

Podobnie jest w przypadku standardowego dynamicznego UITableViewController:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellID = @"CellID";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];

    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1)
    {
        cell.contentView.frame = cell.bounds;
        cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    }
    //your code goes here       
    return cell;
}

W tym przypadku nie potrzebujemy dodatkowej kontroli kompilacji, ponieważ wymagane jest wdrożenie tej metody.

Pomysł jest taki sam dla obu przypadków i dla UICollectionViewCell, jak skomentowano w tym wątku: Problem z autorezizacją ramki UICollectionViewCell contentView w komórce prototypu Storyboard (Xcode 6, iOS 8 SDK) ma miejsce tylko podczas uruchamiania na iOS 7

KoCMoHaBTa
źródło
1
To najlepsza odpowiedź, a przynajmniej najlepsze dla mnie rozwiązanie. Moja komórka (zasady Autolayout) działała świetnie w iOS8 z XCode6 i działała świetnie z iOS7 i 6 z XCode 5. Aktualizuję XCode do XCode6 i nie działa już w iOS6 i 7. Więc dziękuję !!!!
xarly
Nie stanowi to już problemu w Xcode 6.1. Ponadto nie używaj NSFoundationVersionNumber, zobacz nshipster.com/swift-system-version-checking
onmyway133
1
@ onmyway133: Dlaczego nie stanowi to już problemu w Xcode 6.1? Nadal mam problem, nawet jeśli używam Xcode 6.1 i iOS 8.1 SDK. Problem występuje tylko w systemie iOS 7. Zmiana granic lub ustawienie maski autorezowania rozwiązuje problem. Ale zastosowałem podejście przedstawione w najczęściej głosowanej odpowiedzi lub w komentarzach do niej.
testowanie
Tak, to najlepsza odpowiedź na podstawie mojego 3-godzinnego wyszukiwania w Google. Inne podobne „rozwiązania” nie zapewniają pełnej listy cell.contentView.autoresizingMask. Tylko ten działa dla mojego projektu iPada 7.1 utworzonego w Xcode 6.
Golden Thumb
13

Ponieważ komórki są ponownie używane, a wysokość może się zmieniać w zależności od zawartości, myślę, że ogólnie byłoby lepiej ustawić priorytet odstępów na mniejszy niż wymagany.

W twoim przypadku UIImageView ma odstęp 15 do góry, a widok od dołu ma odstęp 0 do dołu. Jeśli ustawisz priorytet tych ograniczeń na 999 (zamiast 1000), aplikacja nie ulegnie awarii, ponieważ ograniczenia nie są wymagane.

Po wywołaniu metody layoutSubviews komórka będzie miała prawidłową wysokość, a ograniczenia będą normalnie spełnione.

Steven
źródło
Jesteś geniuszem! ładne i czyste rozwiązanie dziękuję :)
Blacky
9

Nadal nie znalazłem dobrego rozwiązania dla scenorysów ... Niektóre informacje również tutaj: https://github.com/Alex311/TableCellWithAutoLayout/commit/bde387b27e33605eeac3465475d2f2ff9775f163#commitcomment-4633188

Z tego, co tam doradzają:

self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);

Wywołałem moje rozwiązanie:

  • kliknij prawym przyciskiem myszy scenorys
  • Otwórz jako -> Kod źródłowy
  • wyszukaj tam ciąg „44”
  • to będzie jak

.

<tableView hidden="YES" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="44" ...
    <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatMessageCell" id="bCG-aU-ivE" customClass="ChatMessageCell">
        <rect key="frame" x="0.0" y="22" width="320" height="44"/>

.

  • zamień rowHeight = „44” na rowHeight = „9999”, a height = „44” na wysokość = „9999”
  • kliknij prawym przyciskiem myszy scenorys
  • Otwórz jako -> Interface Builder
  • uruchom aplikację i sprawdź dane wyjściowe
katamfetamina
źródło
To zadziałało dla mnie. Stworzyłem UITableViewController w scenorysie, w którym niektóre komórki zostały utworzone z prototypu, a inne całkowicie w kodzie przez utworzenie wystąpienia klasy.
Atharva
3

Po prostu zwiększ domyślną wysokość komórki. Wygląda na to, że problem polega na tym, że zawartość komórki jest większa niż domyślny (początkowy) rozmiar komórki, co narusza pewne ograniczenia nieujemności do czasu zmiany rozmiaru komórki do jej rzeczywistego rozmiaru.

Vadim Yelagin
źródło
Rzeczywiście, moje komórki są wyższe niż rozmiar domyślny, ale jak to się dzieje, że domyślny rozmiar jest używany, ponieważ delegat tabeli implementuje tableView: heightForRowAtIndexPath:?
Alexis
Prawdopodobnie komórki są tworzone z domyślnym rozmiarem, a następnie zmieniany do rozmiaru rzeczywistego.
Vadim Yelagin
rzeczywiście spełnia swoje zadanie, ale dlaczego nie dzieje się tak na iOS6?
Alexis
3
@jafar iOS 7 zmienił wiele rzeczy związanych z komórkami widoku tabeli. W systemie iOS 7 istnieje teraz widok przewijania (typu UITableViewCellScrollView) między komórką widoku tabeli a contentView; to prawdopodobnie wyjaśnia różnicę między iOS 6 i 7 tutaj.
smileyborg
3

Może ustaw priorytet widoku na większy niż 750 i mniej niż 1000 może rozwiązać.

"<NSLayoutConstraint:0xac43620 V:[UIImageView:0xac47f50(62)]>",
heramerom
źródło
2

Miałem ten sam problem. Moje rozwiązanie oparte na innych powyżej:

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        // initialize my stuff
        [self layoutSubviews]; // avoid debugger warnings regarding constraint conflicts
    }
    return self;
}

Najlepsze, Alessandro

Cormentis
źródło
2
Z dokumentów Apple:You should not call this method directly. If you want to force a layout update, call the setNeedsLayout method instead to do so prior to the next drawing update. If you want to update the layout of your views immediately, call the layoutIfNeeded method.
Ricardo Sanchez-Saez,
Chcę rozpocząć modlitwę do ciebie. Szukałem GODZIN, próbując rozwiązać ten problem, a to rozwiązuje problem (a także ustawienie contentView.bounds na CGRectMake (0, 0, 99999, 99999);) - TO BŁĄD W KODU JABŁEK. Tak więc, z całym szacunkiem nie zgadzam się z dokumentami dotyczącymi jabłek, które mówią, że nie wywołuj tej funkcji. Apple, napraw swoje bzdury.
Ryan Copley
0

Ja też napotkałem ten problem i żadna z sugestii nie pomogła. W moim przypadku miałem komórkę wyboru rozmiaru i zawierała ona wewnątrz collectionView (każda komórka collectionView zawierała obraz w pełnym rozmiarze). Teraz wysokość komórki była nieco większa (60) niż collectionView (50) i obrazy wewnątrz niej (50). Z tego powodu widok collectionView ma wyrównanie do dołu do ograniczenia superview z wartością 10. W takim przypadku otrzymałem ostrzeżenie i jedynym sposobem na naprawienie tego było ustawienie wysokości komórki na taką samą, jak jej collectionView.

Centurion
źródło
0

Skończyło się na tym, że w ogóle nie korzystałem z UITableViewCell w projektancie ... Tworzę tam niestandardowy widok i programowo dodaję go do widoku zawartości komórki ... Z niektórymi kategoriami pomocy nie ma również standardowego kodu ...

Renetik
źródło
0

Jeśli działa dobrze w iOS8 i otrzymasz ostrzeżenie w iOS7, możesz przeszukać kod źródłowy scenorysu i znaleźć właściwy tableviewCell, a następnie dołączyć atrybut rect po linii tableviewCell. Dzięki kuchumovn .

<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" .../>
                                <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
NSDeveloper
źródło