Problema di ridimensionamento automatico del contenuto di UICollectionViewCell Il frame di View nella cella prototipo di Storyboard (Xcode 6, iOS 8 SDK) si verifica quando si esegue solo su iOS 7


158

Sto usando Xcode 6 Beta 3, iOS 8 SDK. Crea Target iOS 7.0 usando Swift. Si prega di fare riferimento al mio problema passo dopo passo con le schermate di seguito.

Ho un UICollectionView in Storyboard. 1 prototipo UICollectionViewCell che contiene 1 etichetta al centro (nessuna regola di ridimensionamento automatico). Lo sfondo viola doveva contrassegnare un contentView che viene generato in fase di esecuzione dalla cella, suppongo. Tale vista verrà ridimensionata correttamente in base al mio UICollectionViewLayoutDelegate alla fine, ma non su iOS 7. Si noti che sto usando Xcode 6 e il problema si verifica solo su iOS 7.

Quando creo l'app su iOS 8. Va tutto bene.

Nota: il viola è il contentView , il blu è il mio pulsante UIB con angolo arrotondato.

http://i.stack.imgur.com/uDNDY.png

Tuttavia, su iOS 7, tutte le visualizzazioni secondarie all'interno della cella si riducono improvvisamente al frame di (0,0,50,50) e non sono più conformi alla mia regola di ridimensionamento automatico.

http://i.stack.imgur.com/lOZH9.png

Presumo che si tratti di un bug in iOS 8 SDK o Swift o forse Xcode?


Aggiornamento 1: questo problema esiste ancora nell'Xcode 6.0.1 ufficiale! Il miglior modo per aggirare è come quello che KoCMoHaBTa ha suggerito di seguito impostando il frame in cellForItem della cellula (devi comunque sottoclassare la tua cellula). Si è scoperto che si tratta di un'incompatibilità tra iOS 8 SDK e iOS 7 (controlla la risposta di ecotax di seguito citata da Apple).

Aggiornamento 2: incolla questo codice all'inizio di cellForItem e le cose dovrebbero andare bene:

/** Xcode 6 on iOS 7 hot fix **/
cell.contentView.frame = cell.bounds;
cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
/** End of Xcode 6 on iOS 7 hot fix **/

1
Ho scoperto che questo problema esiste ancora in Xcode 6 Beta 5. Qualcuno ha riscontrato anche questo?
Il

2
Sto lottando con questo proprio ora nel mio progetto. iOS 7 e iOS 8 creati con Xcode 5 sembrano a posto. iOS 8 creato con Xcode 6 beta 6 sembra a posto. iOS 7 creato utilizzando Xcode 6 beta 6 presenta il problema che stai descrivendo. Usando Reveal vedo che il mio UICollectionViewCell è stato dimensionato correttamente. Ma il contentView della cella non è stato ridimensionato, anche se è padre, UICollectionViewCell ha attivato le subview di ridimensionamento automatico. La dimensione di contentView è impostata su ciò che ha lo storyboard. Non sto usando il layout automatico in questo progetto. Il mio progetto è completamente obiettivo-c.
Del Brown,

2
Volevo solo aggiungere che questo problema esiste ancora a partire dal seme GM Xcode 6 / iOS 8. La risposta di DanielPlamann per forzare il contentViewridimensionamento con la cella funziona bene per risolvere il problema. Immagino che in iOS 8 Apple abbia cambiato qualcosa nel modo in cui vengono gestite le visualizzazioni del contenuto delle celle quando vengono create in Interface Builder (che è comunque un po 'una scatola nera). Ma il fatto che cambi comportamento durante il targeting di iOS 7 è sicuramente un bug.
Stuart,

Lo stesso qui con Xcode 6 GM, layout automatico e una cella basata su pennino. Lo aggiusto appuntando i contentViewbordi ai bordi della cella.
sergiou87,

3
Ho scaricato xcode 6.1 ma vedo ancora lo stesso problema nel simulatore.
Haitao Li,

Risposte:


169

contentView è rotto. Può anche essere risolto in awakeFromNib

objC:

- (void)awakeFromNib {

    [super awakeFromNib];

    self.contentView.frame = self.bounds;
    self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}

Swift3:

override func awakeFromNib() {
    super.awakeFromNib()

    self.contentView.frame = self.bounds
    self.contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}

3
Ha fatto il trucco senza self.contentView.frame = self.bounds; Pensi di averne bisogno? Grazie comunque!
Michal Shatz,

Ciao Michal, d'accordo con te. Ma per essere sicuro al 100%, come funzionerà meglio nei prossimi iOS per aggiungerlo, penso.
Igor Palaguta,

5
Mi piace questa risposta ma devi chiamare [super awakeFromNib]; Inoltre, sto pensando di aggiungere un controllo per iOS 7.1 e meno, perché non sono sicuro di come l'aggiunta di queste maschere di ridimensionamento influenzi il comportamento predefinito su iOS8.
GingerBreadMane,

Se non stai usando pennini o storyboard, questo funziona anche se lo metti in applyLayoutAttributes:
cetcet

Questo non funziona per me. Non sto usando Autolayout !! Qualche assistenza?
Thatzprem,

61

Ho riscontrato lo stesso problema e ho chiesto aiuto a Apple DTS. La loro risposta fu:

In iOS 7, le visualizzazioni del contenuto delle celle si dimensionavano tramite maschere di ridimensionamento automatico. In iOS 8, questo è stato modificato, le celle hanno smesso di usare le maschere di ridimensionamento automatico e hanno iniziato a ridimensionare la visualizzazione del contenuto in layoutSubviews. Se un pennino è codificato in iOS 8 e quindi decodificato su iOS 7, avrai una visualizzazione del contenuto senza una maschera di ridimensionamento automatico e nessun altro mezzo con cui dimensionare se stesso. Quindi, se dovessi mai cambiare il frame della cella, la visualizzazione del contenuto non seguirà.

Le app che verranno ridistribuite su iOS 7 dovranno aggirare il problema dimensionando la visualizzazione del contenuto stessa, aggiungendo maschere di ridimensionamento automatico o aggiungendo vincoli.

Immagino che ciò significhi che non è un bug in XCode 6, ma un'incompatibilità tra l'SDK di iOS 8 e l'SDK di iOS 7, che ti colpirà se esegui l'upgrade a Xcode 6, perché inizierà automaticamente a utilizzare l'SDK di iOS 8.

Come ho commentato prima, la soluzione alternativa Daniel Plamann ha descritto le opere per me. Quelli descritti da Igor Palaguta e KoCMoHaBTa sembrano però più semplici e sembrano avere senso dare la risposta di Apple DTS, quindi li proverò più avanti.


Questo è interessante, ma spero ancora che lo risolvano. Questo comportamento è stato introdotto solo in Xcode 6 GM che ha aggiunto il supporto per iPhone 6. Funzionava bene nei beta precedenti. Ho anche riportato un progetto a una beta precedente dopo averlo notato e ha funzionato come previsto. Spero che tutti presentino bug con Apple su questo problema.
arton,

@arton Ho presentato una segnalazione di bug lo stesso giorno in cui ho chiesto aiuto a DTS. È stato chiuso come duplicato di 18312246. Non sono sicuro di quanto sia utile.
ecotax

Ho usato il lavoro intorno dimensionando ContentView e funziona per me. - (CGSize) collectionViewContentSize {return CGSizeMake (self.collectionView.bounds.size.width, self.collectionView.bounds.size.height); }
Jesús Hurtado,

60

Ho riscontrato lo stesso problema e spero che Apple risolva questo problema con la prossima versione di Xcode. Nel frattempo uso una soluzione alternativa. Nella mia UICollectionViewCellsottoclasse ho appena ignorato layoutSubviewse ridimensionato il contenuto Visualizza manualmente nel caso in cui la dimensione differisca dalla collectionViewCelldimensione.

- (void)layoutSubviews
{
  [super layoutSubviews];

  BOOL contentViewIsAutoresized = CGSizeEqualToSize(self.frame.size, self.contentView.frame.size);

  if( !contentViewIsAutoresized) {
    CGRect contentViewFrame = self.contentView.frame;
    contentViewFrame.size = self.frame.size;
    self.contentView.frame = contentViewFrame;
  }
}

Sì, ho usato un lavoro simile ma l'ho fatto in cellForItem / cellForRow che funziona anche.
lanciato il

1
Questa è la soluzione migliore L'aggiunta di questo in cellForItem / cellForRow causerà strani artefatti se si sta ruotando il dispositivo e le dimensioni della cella cambiano.
spybart,

Ciao! Ho un problema con questo codice. Per la prima volta, il contenuto booleano ViewIsAutoresized sarà vero se viene caricato dallo storyboard o dalla cella del prototipo. Solo quando si esegue un reloadData, il secondo sarà corretto. Quindi non è davvero necessario verificare la dimensione. Invece fai semplicemente: self.contentView.frame = self.bounds;
lanciato il

Confermato: dovremmo farlo in cellForItem o cellForRow perché layoutSubviews viene chiamato solo dopo la restituzione della cella. Qualunque cosa tu faccia prima come disegnare oggetti verrà calcolata erroneamente.
Il

1
Forse è meglio posizionarlo [cell layoutIfNeeded];in cellForItem o cellForRow?
wtorsi,

38

Un'altra soluzione consiste nell'impostare le dimensioni di contentView e le maschere di ridimensionamento automatico -collectionView:cellForItemAtIndexPath:come segue:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

     static NSString *cellID = @"CellID";

     UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];

     // Set contentView's frame and autoresizingMask
     cell.contentView.frame = cell.bounds;
     cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;

     // Your custom code goes here

     return cell;
}

Funziona anche con il layout automatico, poiché le maschere di ridimensionamento automatico vengono tradotte in vincoli.


2
Questa è una soluzione superiore in quanto funziona con qualsiasi tipo di cella senza sottoclasse e non richiede modifiche in più posizioni quando si utilizza più di un tipo di cella nella vista della raccolta.
nacross,

6

In Xcode 6.0.1 contentView per UICollectionViewCell è rotto per i dispositivi iOS7. Può anche essere corretto aggiungendo i vincoli appropriati a UICollectionViewCell e al suo contentView nei metodi awakeFromNib o init.

        UIView *cellContentView = self.contentView;
        cellContentView.translatesAutoresizingMaskIntoConstraints = NO;

        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[cellContentView]|"
                                                                     options:0
                                                                     metrics:0
                                                                       views:NSDictionaryOfVariableBindings(cellContentView)]];
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[cellContentView]|"
                                                                     options:0
                                                                     metrics:0
                                                                       views:NSDictionaryOfVariableBindings(cellContentView)]];

Solo questo funziona ora, devi farlo solo per IOS 8 e devo chiamarlo dopo ogni dequeue! Ma questa non è la soluzione ideale perché la selezione multipla non funziona come dovrebbe visivamente ...
Renetik

La cosa migliore è usare il autolayout davvero, con questo bug risparmierai ironicamente il tempo usando l'evento autolayout in un caso semplice ..
Renetik

La maschera non funziona per me; tuttavia l'impostazione dei vincoli funziona!
entropide

4

Questo non funzionerà correttamente senza nessuna delle altre soluzioni alternative citate a causa di un bug in Xcode 6 GM con il modo in cui Xcode compila i file xib nel formato pennino. Anche se non posso dire con certezza al 100% che è correlato a Xcode e non ha a che fare con il runtime, sono molto fiducioso: ecco come posso mostrarlo:

  1. Build + Esegui l'applicazione in Xcode 5.1.
  2. Vai alla directory dell'applicazione del simulatore e copia il file .nib compilato per lo xib con cui stai riscontrando problemi.
  3. Build + Esegui l'applicazione in Xcode 6 GM.
  4. Ferma l'applicazione.
  5. Sostituisci il file .nib nella cartella del simulatore dell'applicazione appena costruita con il file .nib creato usando Xcode 5.1
  6. Riavvia l'app dal simulatore, NON da Xcode.
  7. La tua cella caricata da quel .nib dovrebbe funzionare come previsto.

Spero che tutti coloro che leggono questa domanda presentino un radar con Apple. Questo è un problema ENORME e deve essere risolto prima della versione finale di Xcode.

Modifica: Alla luce del post di ecotax , volevo solo aggiornarlo per dire che ora sono confermate differenze di comportamento tra la creazione in iOS 8 vs iOS 7, ma non un bug. Il mio hack ha risolto il problema perché la creazione su iOS 7 ha aggiunto la maschera di ridimensionamento automatico alla visualizzazione del contenuto necessaria per far funzionare questo, che Apple non aggiunge più.


Non sono sicuro che possano rilasciare legalmente un xCode diverso dalla versione GM
Mabedan,

Haha andranno in prigione? = P Ma in tutta serietà, certo che possono. Avevano più versioni GM per Mavericks se non ricordi. Non è troppo comune ma può succedere.
Acey

4

Le risposte in questo post funzionano, quello che non ho mai capito è perché funziona.

Innanzitutto, ci sono due "regole":

  1. Per le viste create a livello di codice (Es. [UIView new]), La proprietà translatesAutoresizingMaskIntoConstraintsè impostata suYES
  2. Le viste create nel generatore di interfaccia, con AutoLayout abilitato, avranno la proprietà translatesAutoresizingMaskIntoConstraintsimpostata suNO

La seconda regola non sembra applicarsi alle viste di livello superiore per le quali non si definiscono vincoli. (Es. La visualizzazione del contenuto)

Quando guardi una cella dello Storyboard, nota che la cella non è contentViewesposta. Non stiamo "controllando" il contentView, lo è Apple.

Approfondisci il codice sorgente dello storyboard e scopri come contentViewviene definita la cella:

<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">

Ora le visualizzazioni secondarie della cella (notare il translatesAutoresizingMaskIntoConstraints="NO"):

<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NaT-qJ-npL" userLabel="myCustomLabel">

Il contentViewnon dispone è translatesAutoresizingMaskIntoConstraintsimpostato NO. Inoltre manca la definizione del layout, forse a causa di ciò che ha detto @ecotax .

Se guardiamo dentro contentView, ha una maschera di ridimensionamento automatico, ma nessuna definizione per questo: <autoresizingMask key="autoresizingMask"/>

Quindi ci sono due conclusioni:

  1. contentView translatesAutoresizingMaskIntoConstraintsè impostato su YES.
  2. contentView manca la definizione di un layout.

Questo ci porta a due soluzioni di cui abbiamo parlato.

È possibile impostare manualmente le maschere di ridimensionamento automatico in awakeFromNib:

self.contentView.frame = cell.bounds;
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

Oppure puoi impostare contentView translatesAutoresizingMaskIntoConstraintssu NOin awakeFromNibe definire i vincoli in - (void)updateConstraints.


4

Questa è la versione Swift della risposta di @ Igor, che è stata accettata e grazie per la tua simpatica risposta.

Prima vai alla tua UICollectionViewCellsottoclasse e incolla il seguente codice come è all'interno della classe.

override func awakeFromNib() {
    super.awakeFromNib()
    self.contentView.frame = self.bounds
    self.contentView.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
}

A proposito, sto usando Xcode 7.3.1 e Swift 2.3. La soluzione è testata su iOS 9.3 che funziona perfettamente.

Grazie, spero che questo abbia aiutato.


scusate il mio cattivo inglese, intendevo "funziona come un fascino" :)
Aznix,

@Aznix Nessun problema .. :)
onCompletion

2

In swift, inserisci il seguente codice nella sottoclasse di celle della vista collection:

override var bounds: CGRect {
  didSet {
    // Fix autolayout constraints broken in Xcode 6 GM + iOS 7.1
    self.contentView.frame = bounds
  }
}

Questa era la risposta che stavo cercando. Ho usato per sostituire setBounds in Objective-C per risolvere questo problema (non ero sicuro di come scriverlo in Swift). Grazie :)
Matthew Cawley,

1

Ho scoperto che ci sono anche problemi con il contentViewdimensionamento in iOS 8. Tende a essere organizzato molto tardi nel ciclo, il che può causare conflitti temporanei di vincolo. Per risolvere questo, ho aggiunto il seguente metodo in una categoria di UICollectionViewCell:

- (void)fixupContentView
{
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 80100
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) {
        self.contentView.frame = self.bounds;
        self.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    } else {
        [self layoutIfNeeded];
    }
#endif
#endif
}

Questo metodo dovrebbe essere chiamato dopo aver rimescolato la cella.


Sarebbe l'ideale se questo metodo fosse chiamato automaticamente. Non mi piace, ma questo potrebbe essere fatto sfrigolando dequeueReusableCellWithReuseIdentifier:forIndexPath:.
Phatmann,

Apple sembra aver risolto questo problema nella versione 8.1 dell'SDK iOS in Xcode 6.1 GM (Build 6A1042b). Pertanto ho aggiornato il codice sopra per non eseguire quando si utilizza l'SDK 8.1. Una volta che il tuo team è passato a Xcode 6.1, puoi rimuovere completamente questo hack.
Phatmann,

1

Ho risolto facendo questo:

override func layoutSubviews() {
   contentView.superview?.frame = bounds
   super.layoutSubviews()
}

vedi: qui


-1

Assicurati di selezionare la casella di controllo "Ridimensiona automaticamente le visualizzazioni secondarie" nel pennino per quella cella della vista raccolta. Funzionerà perfettamente su iOS 8 e iOS 7.


No non lo fa. Questa è una cella prototipo integrata.
Il
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.