"Layout automatico ancora richiesto dopo l'esecuzione di -layoutSubviews" con la sottoclasse UITableViewCell


115

Utilizzando XCode 4.5 e iOS 6, sto sviluppando un'app con una semplice visualizzazione tabella con celle personalizzate. L'ho fatto un centinaio di volte in iOS 5 e versioni precedenti, ma per qualche motivo il nuovo sistema di autoLayout mi sta dando molti problemi.

Ho impostato la visualizzazione della tabella e la cella prototipo in IB, ho aggiunto le sottoview e le ho cablate come IBOutlet, quindi ho configurato il mio delegato e dataSource. Tuttavia ora ogni volta che viene recuperata la prima cella cellForRowAtIndexPath, ottengo il seguente errore:

*** Errore di asserzione in - [ShopCell layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2372/UIView.m:5776

*** Chiusura dell'app a causa di un'eccezione non rilevata "NSInternalInconsistencyException", motivo: "Layout automatico ancora richiesto dopo l'esecuzione di -layoutSubviews. L'implementazione di ShopCell di -layoutSubviews deve chiamare super. '

Non ho implementato un metodo -layoutSubviews nella mia cella sottoclasse (ShopCell), e anche quando provo a farlo e aggiungo la super chiamata in quanto suggerisce che ricevo ancora lo stesso errore. Se rimuovo le sottoview dalla cella in IB e lo cambio in un UITableViewCell standard, tutto funziona come previsto, anche se ovviamente non ho dati nelle mie celle.

Sono quasi certo che mi manchi qualcosa di semplice, ma non riesco a trovare alcuna documentazione o guida che suggerisca cosa ho fatto di sbagliato. Qualsiasi aiuto sarebbe apprezzato.

Modifica: ho appena provato a cambiarlo in un UITableViewCell in IB e lasciando tutte le visualizzazioni secondarie in posizione, sempre lo stesso errore.


Prova lldb [[UIWindow keyWindow] _autoLayoutTrace]nell'area del debugger se viene utilizzato il layout automatico.
A-Live

3
Stai usando UIView per la cella personalizzata invece di UITableViewCell? Ho avuto lo stesso problema. Avevo UIView per la cella personalizzata e stavo aggiungendo viste secondarie a quella. Modificato in UITableViewCell e ha funzionato.

Ehi Mike, come definisci i punti vendita? Sono proprietà nel file di implementazione in un'estensione di classe?
kocodude

@ A-Live Ogni volta che provo a usare quel metodo ricevo un errore nel debugger .... questo metodo è ancora valido? Modifica: non importa, è una l minuscola nel layout automatico.
borrrden

deseleziona la casella AutoLayout in Inspector, quindi pulisci ed esegui. funzionerà burbero.
Nico

Risposte:


57

Ho riscontrato lo stesso problema durante l'aggiunta manuale di vincoli nel codice. Nel codice, stavo facendo quanto segue:

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

Ipotesi

Da quello che posso dire, il problema è che quando disabiliti translatesAutoresizingMaskIntoConstraints, UITableViewCell inizia a utilizzare il layout automatico e naturalmente fallisce perché l'implementazione sottostante di layoutSublayersForLayernon chiama super. Qualcuno con Hopper o qualche altro strumento può confermarlo. Dato che stai usando IB probabilmente ti starai chiedendo perché questo sia un problema ... e questo perché l'uso di IB disabilita automaticamente le translatesAutoresizingMaskIntoConstraintsviste a cui aggiunge vincoli (aggiungerà automaticamente un vincolo di larghezza e altezza al loro posto).

Soluzione

La mia soluzione era spostare tutto nel file contentView.

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

Non sono sicuro al 100% se questo funzionerà in Interface Builder, ma se sposti tutto fuori dalla tua cella (supponendo che tu abbia qualcosa direttamente su di esso) allora dovrebbe funzionare. Spero che questo ti aiuti!


4
Avevo anche bisogno di aggiungere subview.translatesAutoresizingMaskIntoConstraints = NO'ogni vista secondaria che stavo aggiungendo a contentView.
Jay Peyer

5
Questo ha funzionato per me. Inoltre, assicurati di non chiamare self.contentView.translatesAutoresizingMaskIntoConstraints = NOil UITableViewCell.
Maurizio

53

Apparentemente, l'implementazione di layoutSubviews di UITableViewCell non chiama super, il che è un problema con il layout automatico. Sarei interessato a vedere se inserire la seguente categoria nei progetti risolve le cose. Ha aiutato in un progetto di prova.

#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

Potrei aggiungere che il problema si è presentato per me quando si utilizza un backgroundView sulla cella della tabella, in quanto viene aggiunto come sottoview alla cella (mentre la maggior parte delle sottoview dovrebbe essere aggiunta al contentView della cella della tabella, che di solito dovrebbe funzionare meglio).

Nota: sembra che questo bug sia stato risolto in iOS7; Sono stato in grado di rimuovere questo codice o almeno aggiungere un controllo di runtime in modo che venga eseguito solo se in esecuzione su iOS6.


La cosa strana è che funziona bene con un semplice UITableViewCell per me, ma non per una sottoclasse ...
borrrden

Lo penseresti, ma tutto quello che ho è un metodo init, nient'altro> <. Non ho mai ignorato le visualizzazioni secondarie del layout nella mia vita ahah. Penso che il problema sia la visualizzazione personalizzata che UITableViewCell utilizza come visualizzazione principale non può utilizzare il layout automatico perché sovrascrive layoutSubviews (quindi quando si tenta di aggiungere vincoli alla visualizzazione radice, fallirà)
borrrden

6
Ho dovuto creare una categoria del genere UITableViewper lo stesso motivo (iOS 6.1 b1)
Joshua J. McKinnon

5
Esiste una soluzione simile per TableHeaderView poiché il problema esiste ancora in ios 7?
Softlion

1
Funziona alla grande. Ho riscontrato questo problema quando ho tentato di centrare una sottoview UIVIew in un UITableView. Anche in iOS 7 si verifica l'asserzione. Ma non si verifica in iOS 8, quindi devono aver risolto il bug.
Jordan H

33

Ho avuto lo stesso bug per alcuni mesi. Ma ho scoperto qual era il problema.

Quando creo un file IB, UIViewviene già aggiunto un file . Se utilizzi questa visualizzazione, l'app non si arresta in modo anomalo quando il layout automatico è disabilitato (ma ci sono altri problemi). Quando si utilizza il layout automatico, è necessario selezionare la giusta vista nella libreria di oggetti: UITableViewCell.

In realtà, si dovrebbe sempre usare questa voce perché tutti subviews vengono aggiunti al contentViewdi UITableViewCell.

È tutto. Andrà tutto bene.


Questa non dovrebbe essere la risposta accettata perché la domanda non riguarda un'implementazione che utilizza IB e perché questo problema può verificarsi quando non si utilizza IB. Se stai facendo le tue visualizzazioni in modo programmatico, la risposta di @ PhilLoden è più fattibile.
Eric dal

Non ho capito la risposta. qualcuno può spiegare più chiaramente? grazie
hasan

Penso di avere questo diritto. è sufficiente controllare la classe att. nell'ispettore di identità nel generatore di interfacce? o quello che è stato aggiunto era di un altro tipo e classe att. è stato aggiornato più tardi? anche questo causa il problema?
hasan

@ hasan83 Puoi effettivamente restituire la cella. Un UITableViewCell è fondamentalmente un UIView con un identificatore di riutilizzo.
Arnaud

17

Ho avuto gli stessi problemi con custom UITableViewHeaderFooterView+ xib.

Ho visto alcune risposte qui, ma ho trovato quale implementazione -layoutSubviewsnella mia classe di visualizzazione del piè di pagina personalizzata risolve il problema:

-(void)layoutSubviews
{
    [super layoutSubviews];
    [self layoutIfNeeded]; // this line is key
}

1
Attenzione che questo può provocare un ciclo infinito e infine EXC_BAD_ACCESS KERN_PROTECTION_FAILURE
mbi

15

Lo vedevo come risultato della modifica dei vincoli nella mia implementazione di layoutSubviews. Spostare la chiamata a super dall'inizio alla fine del metodo ha risolto il problema.


Questo ha funzionato per me. Ho un UICollectionViewCell personalizzato che sto formattando in layoutSubviews. Qualcuno sa perché questa soluzione funziona però?
STANGMMX

@STANGMMX, la risposta di A'sa Dickens spiega perché.
Fábio Oliveira

15

Ha avuto lo stesso problema in iOS 7 (iOS 8 sembra risolverlo). La soluzione per me era chiamare [self.view layoutIfNeeded]alla fine del mio viewDidLayoutSubviewsmetodo.


Grazie. Mi aiuta Ho incontrato questo problema ieri (su iOS 7). aiuta per iOS 7.
Alexander

@MaciejSwic vedi la mia risposta in alto.
Sound Blaster

Questo ha funzionato per me! Utilizzo di iOS 7.1 con Swift. Stavo rimuovendo e aggiungendo un vincolo su viewDidLayoutSubviews. Ho rimosso la super chiamata e ancora non funzionava, ma questa soluzione ha funzionato! dai una foglia a questo dinosauro! :)
jomafer

Ha funzionato anche per me usando iOS 7.1!
fdlr

14

Ho avuto lo stesso problema. Il problema era nel modo in cui stavo creando la cella Xib. Ho creato un Xib come al solito e ho appena cambiato il tipo di "UIView" predefinito nella mia classe UITableViewCell personalizzata. Il modo corretto per è eliminare prima la visualizzazione predefinita e quindi trascinare l'oggetto cella della visualizzazione tabella su xib. Maggiori dettagli qui: http://allplayers.github.io/blog/2012/11/18/Custom-UITableViewCell-With-NIB/


1
Intuizione perfetta! Ci vorrebbe anni per rendersene conto, specialmente perché le mie app non si arresteranno se utilizzassi una UIView con AutoLayout disabilitato.
Guilherme

Super! vedi anche @Arnaud respone di seguito
Lubbo

7

Ho risolto il problema disattivando "Autolayout" per tutte le visualizzazioni secondarie della mia cella di visualizzazione tabella personalizzata.

Nella xib per una cella personalizzata, seleziona una sottoview e deseleziona File Inspector> Interface Builder Document> Use Autolayout


4
Ho fatto lo stesso. Tuttavia, non è proprio una soluzione se si desidera utilizzare il layout automatico
ajmccall,

7

Ho avuto un problema simile non su UITableViewCellma piuttosto su UITableViewse stesso. Poiché è il primo risultato in Google, lo posterò qui. Si è scoperto che viewForHeaderInSectionera questo il problema. Ho creato un UITableViewHeaderFooterViewe impostato translatesAutoresizingMaskIntoConstraintssu NO. Ora arriva la parte interessante:

iOS 7:

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

Se lo faccio, l'app si blocca con

Il layout automatico è ancora richiesto dopo l'esecuzione di -layoutSubviews. L'implementazione di -layoutSubviews di UITableView deve chiamare super.

OK, pensavo non fosse possibile utilizzare il layout automatico su un'intestazione della vista tabella e solo sulle visualizzazioni secondarie. Ma non è tutta la verità come la vedrai in seguito. Per riassumere: non disabilitare la maschera di ridimensionamento automatico per l'intestazione su iOS 7. Altrimenti funziona bene.

iOS 8:

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

Se non lo usassi, otterrei il seguente output:

Impossibile soddisfare i vincoli contemporaneamente.

Per iOS 8 devi disabilitare la maschera di ridimensionamento automatico per l'intestazione.

Non so perché si comporti in questo modo, ma sembra che Apple abbia risolto alcune cose in iOS 8 e il layout automatico funziona in modo diverso su iOS 7 e iOS 8.


Amico, mi salvi la giornata!
Marcin Małysz

5

Come qualcuno sopra ha già affermato, quando crei una vista da utilizzare in un UITableView, devi eliminare la vista creata per impostazione predefinita e trascinare un UITableViewCell o UITableViewHeaderFooterView come vista principale. Tuttavia, c'è un modo per riparare lo XIB nel caso in cui ti fossi perso quella parte. Devi aprire il file XIB in un editor di testo e nel tag root e il suo figlio diretto aggiungere / modificare l'attributo translatesAutoresizingMaskIntoConstraintsin YES, ad esempio

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


2

Sto riscontrando questo e sembra che sia correlato alle sottoclassi UITableViewCell come celle prototipo a cui sono specificamente aggiunte altre sottoclassi UIView personalizzate. Sottolineo la "consuetudine" qui perché ho avuto successo con le celle che hanno solo figli UIKit, ma cade quando si tenta di costruire i vincoli per le visualizzazioni che ho creato su misura, lanciando l'errore indicato nella domanda degli autori.

Ho dovuto separare le mie celle in pennini indipendenti che non utilizzano AutoLayout.

Speriamo che Apple ripulisca questo casino.


2

Aggiungi le tue sottoview al contentView della cella invece che alla cella stessa. Quindi invece di:

[self addSubview:someView];

devi usare

[self.contentView addSubview:someView];


1

Ho riscontrato questo perché avevo inizialmente aggiunto un UIView invece di un UITableViewCell a un file xib.


1

Ho eliminato questo errore scollegando il backgroundViewconnettore dal mio background UIImageViewe il accessoryViewconnettore dalle mie UIButtonpersonalizzazioni. Sospetto che questi non fossero pensati per essere usati nel modo in cui li usavo io.


1

Oggi mi sono imbattuto in questo problema per la prima volta. Fino ad ora ho avuto diverse esperienze nell'utilizzo di prototipi di sottoclassi UITableViewCell, ma non ho mai riscontrato questo problema. Ciò che era diverso nella cella con cui stavo lavorando era che avevo un IBOutlet per -backgroundView che stavo usando per colorare la cella. Ho scoperto che se creavo una nuova proprietà e aggiungevo ancora una nuova UIView che si estendeva per l'intera cella, questa affermazione scompariva. Per verificare che questa fosse la causa, sono tornato ad allegare questa vista alla presa backgroundView e l'affermazione è ricomparso. Finora, nessun altro problema con l'utilizzo di AutoLayout in un prototipo di sottoclasse UITableViewCell da quando ho apportato questa modifica.


1

Non ho ottenuto alcuna soluzione adeguata per questo problema, ma puoi risolverlo utilizzando i frame e non impostando la proprietà translatesAutoresizingMaskIntoConstraints su No (per impostazione predefinita è sì, quindi non impostarla)

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];

0

Ho vissuto la stessa cosa. Si è scoperto che se si aggiunge programmaticamente una vista secondaria dal proprio ShopCell .xib / storyboard, che utilizza il layout automatico, come vista secondaria a un'altra vista, tale eccezione potrebbe essere generata, a seconda di come sono configurati i vincoli. La mia ipotesi è che i vincoli creati in IB siano ciò che crea i problemi quando si aggiunge programmaticamente una vista come sottoview, poiché mantiene i vincoli da viewA -> viewB nel frattempo potresti aggiungere viewB come sottoview di viewC. Hai capito (quella frase mi confonde persino)?

Nella mia situazione, poiché erano le visualizzazioni molto semplici a causare il problema, ho creato le visualizzazioni in modo programmatico e non in IB. Questo l'ha risolto. Potresti estrarre quelle viste in altri file xib e disabilitare il layout automatico per quelli. Immagino che funzionerebbe.


0

In alcune situazioni, questo risolve facilmente il problema del layout (a seconda del layout). All'interno della tua sottoclasse UITableView, in awakeFromNib o init, imposta la maschera di ridimensionamento automatico:

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

Per impostazione predefinita, è impostato su UIViewAutoresizingNone


Questo ha risolto il problema che stavo affrontando. Sto usando il layout automatico all'interno di una cella di tabella combinato con [tableViewCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].heightper ottenere l'altezza, che poi uso in heightForRowAtIndexPath.
NathanAldenSr

0

Nel mio caso,

L'UIImageView di riferimento per il layout automatico per UITableView viene assegnato a backgroundView di UITableView.

self.tableView.backgroundView = self.tableBackgroundImageView;

Quindi, ho rimosso UIImageView per backgroundView da UIView (visualizzazione radice) e ho ripristinato (rimosso) tutti i riferimenti di layout automatico a tale UIImageView. Ho posizionato UIImageView per lo sfondo all'esterno di UIView (vista radice). Quindi assegnare a backgroundView di UITableView nel codice.

Quindi risolto.


0

Ho trovato la soluzione.

Nel mio caso, ho creato la vista della cella nello storyboard (con layout automatico abilitato) e ho definito l'interfaccia UITableViewCell personalizzata nel mio ViewController.m, devo spostare l'interfaccia su ViewController.h.


0

Ho riscontrato lo stesso problema quando utilizzo lo storyboard per creare l'UITableViewCell personalizzato. Fortunatamente ho riscontrato il problema, perché ho scaricato l'accessorioView ([UITableViewCell setAccessoryView:]) su UIButton che ho aggiunto alla cella.

Quindi si è verificato nel mio progetto quando eseguito su iOS6.

Soluzione

Rilascio la presa tra la vista accessoria e il mio pulsante che conteneva la cella personalizzata.

Proposta

Non dovresti usare gli elementi nativi di UITableViewCell e cambiarlo.


0

Questo problema può essere causato dimenticando di chiamare [super viewDidAppear:]all'interno viewDidAppear, ma sono sicuro che non sia l'unica causa.


0

Ho avuto esattamente lo stesso problema. Ecco il problema con il mio progetto:
quando ho lavorato su Interface Builder per creare un UITableViewCell personalizzato, ho trascinato una vista invece di una cella di visualizzazione tabella dal riquadro di raccolta oggetti in Xcode
come cella della tabella personalizzata.
Se ti trovi nella stessa situazione, ecco la soluzione:
elimina la vista nel generatore di interfacce, assicurati di trascinare una cella della vista tabella dal riquadro di raccolta degli oggetti e ripetere la visualizzazione della cella della tabella personalizzata. Puoi copiare gli oggetti nella vecchia vista e incollarli nell'area di disegno per la nuova cella della vista tabella.


0

Ho avuto un problema molto simile con una visualizzazione del piè di pagina della tabella che stavo impostando in Xcode 6, iOS 7+. La soluzione era nel formato del file pennino. Apparentemente era bloccato nel formato Xcode 4 o qualcosa del genere. Modificando le impostazioni del file in "opens in: Xcode 6.0" (o Default, se è per questo), il problema è stato risolto immediatamente. Ho trovato la soluzione per caso: mi stava facendo impazzire, quindi ho cancellato l'intero file e l'ho creato di nuovo, ovviamente con le impostazioni di default. Non ho idea del motivo per cui la semplice modifica del file nell'ultimo Xcode non lo abbia convertito in un formato Xcode 5+, come accade di solito.

f


0

Ho avuto lo stesso problema. Sono andato al mio DetailViewController e ho rinominato l'identificatore in UIView. In precedenza era su UITableView. Ha risolto il problema. Questo problema non deve essere nel tuo DetailViewController. Potrebbe essere in qualsiasi altro. Prova a rinominarlo con l'identificatore rispettato.


0

Ho avuto un problema simile con le celle di visualizzazione tabella statica in IB. Una delle celle aveva una vista secondaria che aveva una classe che era stata erroneamente modificata in una sottoclasse di UITextfield. Il compilatore non ha fornito alcun avviso / errore. Ma in fase di esecuzione il sistema non è stato in grado di caricare il controller della vista con il suddetto arresto anomalo di conseguenza.


0

Il problema è la sequenza delle chiamate di layout alle viste secondarie:

Check-out

Si presenta in iOS <8


0

Soluzione: modificare i vincoli prima di chiamare super layoutSubviews

- (void)layoutSubviews
{

    [self _updateConstraints];

    [super layoutSubviews];
}

0

Ho modificato la risposta di Carl Lindberg per ignorare UITableViewinvece e ha iniziato a funzionare per me:

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

Quindi in MyViewController.mho appena importato la categoria:

#import "UITableView+AutoLayoutFix.h"

0

Ho incontrato lo stesso problema e alla fine ho scoperto che il motivo era che ho aggiunto un vincolo a UITableViewCell, che dovrebbe essere il contentView di UITableViewCell . Quando ho cambiato il vincolo è andato tutto bene!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.