Problema di vincoli di layout automatico su iOS7 in UITableViewCell


104

Sto usando vincoli di layout automatico a livello di codice per creare il layout delle mie celle UITableView personalizzate e sto definendo correttamente le dimensioni delle celle in tableView:heightForRowAtIndexPath:

Si sta lavorando bene su iOS6 e lo fa guardare bene in iOS7 pure

MA quando eseguo l'app su iOS7, ecco il tipo di messaggio che vedo nella console:

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

E in effetti c'è uno dei vincoli in quella lista che non voglio:

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

e non posso impostare la translatesAutoresizingMaskIntoConstraintsproprietà del contentViewsu NO => rovinerebbe l'intera cella.

44 è l'altezza della cella predefinita ma ho definito le mie altezze personalizzate nel delegato della vista tabella, quindi perché la cella contentView ha questo vincolo? Cosa potrebbe causare questo?

In iOS6 non sta succedendo e tutto sembra a posto sia su iOS6 che su iOS7.

Il mio codice è abbastanza grande, quindi non lo pubblicherò qui, ma sentiti libero di chiedere un pastebin se ne hai bisogno.

Per specificare come lo sto facendo, sull'inizializzazione delle cellule:

  • Creo tutte le mie etichette, pulsanti, ecc
  • Ho impostato la loro translatesAutoresizingMaskIntoConstraintsproprietà su NO
  • Li aggiungo come sottoviste della contentViewcella
  • Aggiungo i vincoli su contentView

Sono anche profondamente interessato a capire perché questo accade solo su iOS7.


Su cosa è impostata l'altezza della cella personalizzata?
Mike Pollard

la mia altezza della cella personalizzata è impostata su 90
Alexis

Un collega ha avuto lo stesso problema ieri, ma con la larghezza predefinita di 320 (per un'app per iPad).
jrturton

C'è un progetto di esempio qui che dimostra lo stesso problema: github.com/Alex311/TableCellWithAutoLayout Ecco alcune delle mie osservazioni su di esso: github.com/Alex311/TableCellWithAutoLayout/commit/…
smileyborg

Mi sono imbattuto nello stesso errore, ma è stato perché non ho mai inizializzato la cella prototipo aggiuntiva che stavo usando per trovare l'altezza della mia cella personalizzata.
Steve Moser

Risposte:


132

Ho avuto anche questo problema .. Sembra che il frame di contentView non venga aggiornato fino a quando non layoutSubviewsviene chiamato, tuttavia il frame della cella viene aggiornato prima, lasciando il frame di contentView impostato {0, 0, 320, 44}al momento in cui vengono valutati i vincoli.

Dopo aver esaminato il contentView in modo più dettagliato, sembra che le maschere di ridimensionamento automatico non siano più impostate.

L'impostazione della maschera di ridimensionamento automatico prima di limitare le visualizzazioni può risolvere questo problema:

- (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;
}

Questo fa il lavoro, grazie amico. Cosa potrebbe accadere quando si usano gli stili di modifica?
Alexis

11
@ L14M333 Vedi il codice che ho pubblicato in questo commento qui - in pratica, dovresti essere in grado di impostare self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);prima di aggiungere i tuoi vincoli, il che dovrebbe risolvere il problema.
smileyborg

2
autoresizingMask non funziona per me: il vincolo iniziale è scomparso ma vengono aggiunti i 3 nuovi "<NSAutoresizingMaskLayoutConstraint: 0x8b79740 h = - & - v = - & - UITableViewCellContentView: 0x8b44010.height == UITableViewCellScrollView: 0x8heb4a>" "<NSAutoresizingMaskLayoutConstraint: 0x8b7b9f0 h = - & - v = - & - UITableViewCellScrollView: 0x8b4a130.height == LibraryCell: 0x8ac19d0.height>", "<NSAutoresizingMaskLayoutConstraint: 0x8 --7 = 0x8ac19d0 (0)]> "
catamphetamine

1
Funziona anche per me, ho lottato per circa un'ora! Grazie! <3
mokagio

2
Dopo essermi sbarazzato di altri problemi di layout automatico (iOS 7 / iOS 8) ho scoperto che usare self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);funziona bene come usare self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;. L'ho inserito updateConstraintsprima di aggiungere i vincoli alla visualizzazione del contenuto.
test

35

Apparentemente c'è qualcosa di sbagliato in UITableViewCell e UICollectionViewCell su iOS 7 che utilizzano iOS 8 SDK.

Puoi aggiornare il contentView della cella quando la cella viene riutilizzata in questo modo:

Per UITableViewController statico:

#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

Poiché i controller di visualizzazione tabella statica sono fragili e possono essere facilmente interrotti se si implementano alcuni metodi di origine dati o di eliminazione, sono disponibili controlli che assicureranno che questo codice venga compilato ed eseguito solo su iOS 7

È simile per UITableViewController dinamico standard:

-(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;
}

In questo caso non è necessario il controllo aggiuntivo della compilazione, poiché è richiesta l'implementazione di questo metodo.

L'idea è la stessa per entrambi i casi e per UICollectionViewCell, come commentato in questo thread: Problema di ridimensionamento automatico del frame di UICollectionViewCell contentView nella cella prototipo di Storyboard (Xcode 6, iOS 8 SDK) si verifica quando viene eseguito solo su iOS 7


1
Questa è la migliore risposta, o almeno la migliore soluzione per me. My Cell (regole di layout automatico) ha funzionato benissimo in iOS8 con XCode6 e ha funzionato benissimo con iOS7 e 6 con XCode 5. Ho aggiornato XCode a XCode6 e non funziona più in iOS6 e 7. Quindi, grazie !!!!
xarly

Questo non è più un problema con Xcode 6.1. Inoltre, non utilizzare NSFoundationVersionNumber, consulta nshipster.com/swift-system-version-checking
onmyway133

1
@ onmyway133: Perché questo non è più un problema in Xcode 6.1? Ho ancora il problema anche se utilizzo Xcode 6.1 e iOS 8.1 SDK. Il problema si verifica solo su iOS 7. La modifica dei limiti o l'impostazione di una maschera di ridimensionamento automatico risolve il problema. Ma ho utilizzato l'approccio indicato nella risposta più votata o nei suoi commenti.
test

Sì, questa è la risposta migliore in base alle mie 3 ore di ricerca su Google. Altre "soluzioni" simili non forniscono l'elenco completo di cell.contentView.autoresizingMask. Solo questo funziona per il mio progetto iPad 7.1 creato in Xcode 6.
Golden Thumb

13

Poiché le celle vengono riutilizzate e l'altezza può cambiare in base al contenuto, penso che in generale sarebbe meglio impostare la priorità delle spaziature su un valore inferiore a quello richiesto.

Nel tuo caso, UIImageView ha una spaziatura di 15 verso l'alto e la vista inferiore ha una spaziatura di 0 verso il basso. Se imposti la priorità di questi vincoli su 999 (invece di 1000), l'app non si arresta in modo anomalo, perché i vincoli non sono obbligatori.

Una volta chiamato il metodo layoutSubviews, la cella avrà l'altezza corretta e i vincoli potranno essere soddisfatti normalmente.


Sei un genio! soluzione bella e pulita grazie :)
Blacky

9

Ancora non ho trovato una buona soluzione per gli storyboard ... Alcune informazioni anche qui: https://github.com/Alex311/TableCellWithAutoLayout/commit/bde387b27e33605eeac3465475d2f2ff9775f163#commitcomment-4633188

Da quello che consigliano lì:

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

Ho indotto la mia soluzione:

  • fare clic con il pulsante destro del mouse sullo storyboard
  • Apri come -> Codice sorgente
  • cerca lì la stringa "44"
  • sarà come

.

<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"/>

.

  • sostituire rowHeight = "44" con rowHeight = "9999" e altezza = "44" con altezza = "9999"
  • fare clic con il pulsante destro del mouse sullo storyboard
  • Apri come -> Interface Builder
  • esegui la tua app e controlla l'output

Questo ha funzionato per me. Avevo creato un UITableViewController nello storyboard in cui alcune celle sono state create dal prototipo e altre interamente in codice istanziando una classe.
Atharva

3

Basta aumentare l'altezza della cella predefinita. Sembra che il problema sia che il contenuto della cella è più grande della dimensione della cella predefinita (iniziale), violando alcuni vincoli di non negatività fino a quando la cella non viene ridimensionata alla dimensione effettiva.


In effetti le mie celle sono più alte della dimensione predefinita, ma come mai viene utilizzata la dimensione predefinita poiché il delegato di tableview implementa tableView: heightForRowAtIndexPath:?
Alexis

Probabilmente le celle vengono create con la dimensione predefinita e quindi ridimensionate alla dimensione effettiva.
Vadim Yelagin

in effetti fa il lavoro, ma perché non succede su iOS6 allora?
Alexis

3
@jafar iOS 7 ha cambiato molte cose intorno alle celle della vista tabella. Su iOS 7, ora c'è una visualizzazione a scorrimento (di tipo UITableViewCellScrollView) tra la cella della visualizzazione tabella e la visualizzazione del contenuto; questo probabilmente spiega la differenza tra iOS 6 e 7 qui.
smileyborg

3

Forse impostare la priorità della vista su un valore superiore a 750 e inferiore a 1000 può essere risolto.

"<NSLayoutConstraint:0xac43620 V:[UIImageView:0xac47f50(62)]>",

2

Ho avuto lo stesso problema. La mia soluzione basata su altri sopra:

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

Cordiali saluti, Alessandro


2
Dai documenti di 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

Voglio iniziare una religione pregando per te. Ho cercato HOURS cercando di risolvere questo problema e questo lo risolve (oltre a impostare contentView.bounds su CGRectMake (0, 0, 99999, 99999);) - QUESTO È UN BUG NEL CODICE DELLE MELE. Quindi, sono rispettosamente in disaccordo con i documenti di mele che dicono di non chiamare quella funzione. Apple, aggiusta la tua merda.
Ryan Copley

0

Ho anche affrontato questo problema e nessuno dei suggerimenti ha aiutato. Nel mio caso avevo una cella di selezione delle dimensioni e conteneva collectionView all'interno (ogni cella collectionView conteneva un'immagine a grandezza intera). Ora, l'altezza delle dimensioni della cella era un po 'più grande (60) della collectionView (50) e delle immagini al suo interno (50). Per questo motivo, la vista collectionView ha allineato la parte inferiore al vincolo di superview con un valore di 10. In questo caso è un avviso e l'unico modo per risolvere questo problema era rendere l'altezza della cella uguale alla sua collectionView.


0

Ho finito per non usare affatto UITableViewCell in designer ... Creo lì una visualizzazione personalizzata e la aggiungo alla visualizzazione del contenuto della cella a livello di codice ... Con alcune categorie di aiuto non c'è nemmeno il codice boilerplate ...


0

Se funziona correttamente in iOS8 e ricevi l'avviso in iOS7, puoi cercare nel codice sorgente dello storyboard e trovare il tableviewCell corretto, quindi aggiungere l'attributo rect dopo la riga tableviewCell. Grazie a kuchumovn .

<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" .../>
                                <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
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.