Perfektionieren Untersicht Größenänderung in UICollectionView layout-übergang
Bin ich, eine app zu entwickeln, die eine UICollectionView
- die collection view job ist zur Anzeige von Daten aus einer web-service.
Eine Besonderheit der app, die ich versuche zu implementieren ist, so dass der Benutzer ändern Sie das layout dieser UICollectionView
aus der grid-Ansicht zur Tabellenansicht.
Ich verbrachte viel Zeit damit, zu perfekt und ich schaffte es, um es zu arbeiten. Jedoch gibt es einige Probleme. Der übergang zwischen den beiden layout sieht nicht gut aus und manchmal sind es die Pausen zwischen den wechseln von Ansichten und meine app ist Links mit einer Ansicht in einem unerwarteten Zustand sind. Das passiert nur, wenn der Benutzer schaltet zwischen raster-und Tabellen-Ansicht sehr schnell (durch drücken der changeLayoutButton
) kontinuierlich.
So, natürlich gibt es einige Probleme und ich habe das Gefühl, der code ist ein wenig zerbrechlich. Ich muss auch zum lösen der oben genannten Probleme.
Ich beginnen werde, wie ich implementiert diese Ansicht.
Umsetzung
Da brauchte ich die zwei verschiedenen Zellen (grideCell
und tableViewCell
), um andere Dinge - die ich beschlossen, es wäre besser, Unterklasse UICollectionViewFlowLayout
da es tut alles was ich brauche - alles, was ich tun müssen, ist ändern des Zellen-Größen.
Damit im Sinn ich habe zwei Klassen, Unterklassen UICollectionViewFlowLayout
Dies ist, wie diese zwei-Klassen Aussehen:
BBTradeFeedTableViewLayout.m
#import "BBTradeFeedTableViewLayout.h"
@implementation BBTradeFeedTableViewLayout
-(id)init
{
self = [super init];
if (self){
self.itemSize = CGSizeMake(320, 80);
self.minimumLineSpacing = 0.1f;
}
return self;
}
@end
BBTradeFeedGridViewLayout.m
#import "BBTradeFeedGridViewLayout.h"
@implementation BBTradeFeedGridViewLayout
-(id)init
{
self = [super init];
if (self){
self.itemSize = CGSizeMake(159, 200);
self.minimumInteritemSpacing = 2;
self.minimumLineSpacing = 3;
}
return self;
}
@end
Sehr einfach, und wie Sie sehen können - nur die änderung der Zellgröße.
Dann in meinem viewControllerA
Klasse I implementiert die UICollectionView
etwa so:
Erstellt die Eigenschaften:
@property (strong, nonatomic) BBTradeFeedTableViewLayout *tableViewLayout;
@property (strong, nonatomic) BBTradeFeedGridViewLayout *grideLayout;
in viewDidLoad
/* Register the cells that need to be loaded for the layouts used */
[self.tradeFeedCollectionView registerNib:[UINib nibWithNibName:@"BBItemTableViewCell" bundle:nil] forCellWithReuseIdentifier:@"TableItemCell"];
[self.tradeFeedCollectionView registerNib:[UINib nibWithNibName:@"BBItemGridViewCell" bundle:nil] forCellWithReuseIdentifier:@"GridItemCell"];
Den Benutzer auf eine Schaltfläche tippt, um zwischen layouts:
-(void)changeViewLayoutButtonPressed
Benutze ich einen BOOL um festzustellen, welches layout gerade aktiv ist, und, basierend auf, dass ich den Schalter mit diesem code:
[self.collectionView performBatchUpdates:^{
[self.collectionView.collectionViewLayout invalidateLayout];
[self.collectionView setCollectionViewLayout:self.grideLayout animated:YES];
} completion:^(BOOL finished) {
}];
In cellForItemAtIndexPath
Ich bestimmen, welche Zellen ich verwenden soll (raster-oder tableView) und das laden der Daten -, der code sieht wie folgt aus:
if (self.gridLayoutActive == NO){
self.switchToTableLayout = NO;
BBItemTableViewCell *tableItemCell = [collectionView dequeueReusableCellWithReuseIdentifier:tableCellIdentifier forIndexPath:indexPath];
if ([self.searchArray count] > 0){
self.switchToTableLayout = NO;
tableItemCell.gridView = NO;
tableItemCell.backgroundColor = [UIColor whiteColor];
tableItemCell.item = self.searchArray[indexPath.row];
}
return tableItemCell;
}else
{
BBItemTableViewCell *gridItemCell= [collectionView dequeueReusableCellWithReuseIdentifier:gridCellIdentifier forIndexPath:indexPath];
if ([self.searchArray count] > 0){
self.switchToTableLayout = YES;
gridItemCell.gridView = YES;
gridItemCell.backgroundColor = [UIColor whiteColor];
gridItemCell.item = self.searchArray[indexPath.row];
}
return gridItemCell;
}
Schließlich in der Zelle zwei-Klassen - ich benutze nur die Daten um das Bild /der text, wie ich brauche.
Auch in der grid-Zelle - das Bild ist größer und ich Entferne den text will ich nicht - das war der primäre Grund für verwendet zwei Zellen.
Würde mich interessieren, wie dieser Blick sich ein wenig mehr Flüssigkeit und weniger buggy in der Benutzeroberfläche. Der Blick, den ich gehe, ist nur wie eBays iOS-app - wechseln Sie zwischen drei verschiedenen Ansichten. Ich muss nur zum wechseln zwischen zwei verschiedenen Ansichten.
- Haben Sie versucht, deaktivieren Sie die Schaltfläche "layout" für den übergang ist passiert?
- Das könnte funktionieren - aber fühle ich mich seiner hack. Ich möchte die tradition als ganzes robuster.
- Es ist nicht ein Angriff auf alle. Wenn Sie darüber nachdenken - Sie nicht wollen, zu übertragen und zurück, während in der Mitte der original-animation.
- Ich nehme an, du hast Recht. Würde ich es deaktivieren, während der animation und aktivieren Sie in der Abschluss-block?
- Genau das richtige
Du musst angemeldet sein, um einen Kommentar abzugeben.
Grid /Tabelle übergänge sind nicht so einfach wie eine triviale demo hätten Sie glauben. Sie funktioniert gut, wenn Sie haben, ein einzelnes Etikett in der Mitte der Zelle und einen einfarbigen hintergrund, aber sobald Sie haben alle real Inhalte gibt, ist es vorbei fällt. Dies ist der Grund, warum:
Gibt es viele verschiedene Lösungen. Es ist schwer, etwas zu empfehlen, bestimmte ohne zu sehen, Ihre Zellinhalte, aber ich habe Erfolg mit der folgenden:
Das ist der Ansatz, den ich verwendet hier zu ziemlich guten Effekt.
Ist es schwieriger zu arbeiten, dass das Vertrauen auf die Größenänderung Masken oder Autolayout aber es ist die zusätzliche Arbeit, dass macht die Dinge gut Aussehen.
Wie für die Ausgabe, wenn der Benutzer die Umschaltung zwischen den layouts zu schnell - deaktivieren Sie kurz die-Taste, wenn der übergang beginnt, und re - aktivieren, wenn Sie fertig sind.
Als praktischer Beispiel, hier ist ein Beispiel das layout ändern (etwas davon ist weggelassen) aus dem app oben verlinkten. Beachten Sie, dass die Interaktion deaktiviert, wenn der übergang Auftritt, ich bin mit dem übergang layout aus dem oben verlinkten Projekt, und es ist ein Abschluss-handler:
Innerhalb der Zelle, die die gleiche Zelle, die entweder für das layout, habe ich ein Bild anzeigen und ein label-container. Der label-container enthält alle Beschriftungen und legt Sie intern mit auto-layout". Es gibt Konstante frame-Variablen für die Bild-Ansicht und der label-container in den einzelnen Layouts.
Layout-Attribute aus dem übergang layout sind eine benutzerdefinierte Unterklasse, die einen übergang Fortschritt Unterkunft in der update-layout-Attribute block oben. Diese übergeben wird in die Zelle über die applyLayoutAttributes Methode (ein anderer code weggelassen):
layoutSubviews
in der Zelle Unterklasse übernimmt die harte Arbeit und die Interpolation zwischen den beiden frames für die Bilder und Etiketten, wenn ein übergang im Gange ist:... Und das ist über es. Im Grunde benötigen Sie eine Möglichkeit zu sagen der Zelle, wie weit durch den übergang ist es, was Sie brauchen, die layout-Umstellung-Bibliothek (oder, wie ich sage, Facebook pop könnte dies tun), und dann müssen Sie sicherstellen, dass Sie schöne Werte für das layout beim Wechsel zwischen den beiden.
@jrturton die Antwort ist hilfreich, aber es sei denn, ich bin fehlt etwas, es ist wirklich overcomplicating etwas sehr einfaches. Ich beginne mit den Punkten, wir Stimmen...
Verhindern, dass Interaktion beim ändern des layouts
First off, ich Stimme mit dem Ansatz überein deaktivieren der Benutzer-Interaktion zu Beginn des layout-übergangs - & neuaktivieren am Ende (in der Abschluss-block) mit
[[UIApplication sharedApplication] begin/endIgnoringInteractionEvents]
- das ist viel besser als zu versuchen, Abbrechen, ein in-progress-transition animation & sofort beginnen die umgekehrte übergang von dem aktuellen Zustand.Vereinfachung der layout-übergang durch die Verwendung einer einzigen Zelle-Klasse
Auch ich sehr einverstanden mit dem Vorschlag, die Verwendung der gleichen Zellen-Klasse für jedes layout. Registrieren Sie eine einzelne Zelle Klasse
viewDidLoad
, und vereinfachen Sie IhrecollectionView:cellForItemAtIndexPath:
Methode nur aus der Warteschlange entfernt, eine Zelle und legen Sie seine Daten:(Beachten Sie, dass die Zelle sich nicht (außer in Ausnahmefällen) müssen sich bewusst sein, alles zu tun, was layout ist derzeit im Einsatz, ob layouts überführen, oder was der aktuelle übergang Fortschritt ist)
Dann, wenn Sie anrufen
setCollectionViewLayout:animated:completion:
der Ansicht Sammlung nicht brauchen, um neu zu laden keine neuen Zellen, es stellt nur eine animation block-zum ändern aller Zellen im layout-Attribute (die Sie nicht brauchen, um diese Methode aufzurufen, die von innen eineperformBatchUpdates:
block, und du musst auch nicht entkräften, das layout manuell).Animieren die Zelle Untersichten
Jedoch darauf hingewiesen, Sie werden feststellen, dass die subviews der Zelle springen sofort auf Ihre neues layout für die frames. Die Lösung ist einfach die Kraft die sofortige Anordnung der Zellen Untersichten, wenn die layout-Attribute aktualisiert:
(Keine Notwendigkeit zu schaffen, spezielle layout-Attribute nur für den übergang)
Warum funktioniert das? Wenn die Sammlung die Anzeige von änderungen, layouts,
applyLayoutAttributes:
heißt für jede Zelle die Auflistung anzeigen ist die Einstellung der animation block für diesen übergang. Aber das layout der Zelle ist Untersichten nicht sofort erfolgt ist - es wird verzögert zu einem späteren run loop - was in der tatsächlichen Untersicht layout-änderungen, die nicht integriert werden in die animation blockiert, so dass die Untersichten springen auf den letzten Positionen unmittelbar. AufruflayoutIfNeeded
bedeutet, dass wir sagen, der Zelle, dass wir wollen, dass die Untersicht layout geschehen sofort, so das layout ist innerhalb der animation block, und die Untersichten' frames animiert werden, zusammen mit der Zelle selbst.Es ist wahr, dass die Verwendung der standard -
setCollectionViewLayout:...
API einschränken, Kontrolle über die animation-timing. Wenn Sie sich bewerben möchten eine benutzerdefinierte easing-animation Kurve dann Lösungen wieTLLayoutTransitioning
zeigen eine praktische Möglichkeit, unter Ausnutzung der interaktivenUICollectionViewTransitionLayout
Objekte, die Kontrolle über die animation-timing. Solange jedoch nur eine lineare animation von Untersichten ist erforderlich, ich denke, die meisten Leute zufrieden sein werden mit der Standard-animation, vor allem angesichts der one-line-Einfachheit in der Umsetzung.Für das Protokoll, ich bin nicht scharf auf die mangelnde Kontrolle dieser animation selbst, so umgesetzt, etwas ähnliches zu
TLLayoutTransitioning
. Wenn das für Euch auch gilt, dann ignorieren Sie bitte meine harte reproval von @jrturton ansonsten Super Antwort, und schaue inTLLayoutTransitioning
oderUICollectionViewTransitionLayout
s realisiert mit Timer 🙂layoutIfNeeded()
imapplyLayoutAttributes()
funktioniert wunderbar. Ich bin sehr froh, dass ich nicht zu greifen, zu TLLayoutTransitioning.layoutIfNeeded
.