7

Ich verwende eine UICollectionView mit einem benutzerdefinierten Layout, das Zellen in einem Rasterformat darstellt. Es kann weit über 50 Zeilen und 50 Spalten geben. Scrollen findet sowohl vertikal als auch horizontal statt. Derzeit bin ich das gesamte Layout-Setup in prepareLayout tun und es in Arrays zu speichern:UICollectionView Sehr langsames benutzerdefiniertes Layout

- (void)prepareLayout { 

    NSMutableArray *newLayoutInfo = [[NSMutableArray alloc] init]; 
    NSMutableArray *newLinearLayoutInfor = [[NSMutableArray alloc] init]; 

    NSInteger sectionCount = [self.collectionView numberOfSections]; 
    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0]; 

    self.heightForRows = [delegate collectionViewHeightForAllRows]; 

    self.totalWidthsForRows = [[NSMutableArray alloc] init]; 

    for (int i = 0; i < sectionCount; i++) { 
     [self.totalWidthsForRows addObject:[NSNumber numberWithInt:0]]; 
    } 
    for (NSInteger section = 0; section < sectionCount; section++) { 
     NSMutableArray *cellLayoutInfo = [[NSMutableArray alloc] init]; 


     NSInteger itemCount = [self.collectionView numberOfItemsInSection:section]; 

    for (NSInteger item = 0; item < itemCount; item++) { 
     indexPath = [NSIndexPath indexPathForItem:item inSection:section]; 

     UICollectionViewLayoutAttributes *itemAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; 
     itemAttributes.frame = [self frameForCellAtIndexPath:indexPath]; 

     [cellLayoutInfo addObject:itemAttributes]; 
     [newLinearLayoutInfor addObject:itemAttributes]; 
    } 
    [newLayoutInfo addObject:cellLayoutInfo]; 
} 
self.layoutInfo = newLayoutInfo; 
self.linearLayoutInfo = newLinearLayoutInfor; 
} 

Dann in layoutAttributesForElementsInRect ich habe:

- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect { 
NSArray *rows = [self.linearLayoutInfo filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(UICollectionViewLayoutAttributes *evaluatedObject, NSDictionary *bindings) { 
    return CGRectIntersectsRect(rect, [evaluatedObject frame]); 
}]]; 

Das funktioniert gut, aber es ist laggy und schreckhaft, wenn ich habe über 50 Spalten und 50 Zeilen. Das Problem, das ich jetzt habe, ist, dass ich

-(BOOL)shouldInvalidateLayoutForBoundsChange { 
     return YES; 
} 

Dies macht gesetzt muss das gesamte Layout jedes Mal die Grenzen Veränderung, die, unnötig zu sagen, hat einen großen Einfluss auf die Leistung vorbereiten und man kann kaum bewegen. Die Zellen bestehen nur aus Text mit einem undurchsichtigen Hintergrund, daher gibt es dort kein Problem.

Ich bin sicher, ich mache das nicht richtig und es muss einen besseren Weg geben. Danke für die Hilfe im Voraus.

+2

Sie Instrumente verwenden sollten, um zu sehen, wo Sie Ihre Zeit verbringen während des Scrollens. – nielsbot

+0

Ich empfehle, Elemente so anzuordnen, wie sie Ihrer Sammlungsansicht hinzugefügt werden. Keine Notwendigkeit, Elemente, die bereits vorhanden sind, anzuordnen - sie befinden sich an den richtigen Positionen. Wenn Sie es auf diese Weise tun, können Sie 'shouldInvalidateLayoutForBoundsChange' deaktivieren. – nielsbot

+0

@nielsbot Ich habe ergänzende Ansichten, die als Kopfzeilen fungieren, die immer auf den Inhalts-Offset der Sammelansicht gesetzt werden müssen. Diese befinden sich oben und an der Seite der Ansicht. Wenn ich 'shouldInvalidateLayoutForBoundsChange' abwende," floaten "die Header nicht mehr dort, wo sie sollen. Gibt es dafür eine andere Lösung? –

Antwort

0

ok - ich verstehe jetzt. Ich empfehle Folgendes: Erstellen Sie 3 Sammlungsansichten ... eine für die Spaltenüberschriften (wobei jede Zelle die Spaltenüberschrift ist), eine für die Zeilenüberschriften (jede Zelle = 1 Zeilenvorspann) und eine Sammlungsansicht für Ihre Zellen. Wenn die Bildlaufposition einer Sammlungsansicht vom Benutzer geändert wird, aktualisieren Sie die Bildlaufpositionen für die anderen beiden Sammlungsansichten entsprechend.

enter image description here

+0

sollten Sie Instrumente verwenden, um Ihre App während des Scrollens zu profilieren und zu sehen, wo Sie Ihre CPU-Zeit verbringen. Wenn Sie im Hauptthread CPU-intensiv arbeiten, kann die Scrollleistung beeinträchtigt werden. – nielsbot

+2

Ich habe auch einen Profiler gemacht, den Sie während Ihrer Draw/Layout-Routinen verwenden können, die helfen könnten: https://github.com/nielsbot/Profiler – nielsbot

+4

Machst du Witze? 3 Sammelansichten ?? Die Sammlungsansicht soll jede Art von UI mit ihren benutzerdefinierten Layoutunterklassen entwerfen. 3 Sammlungsansichten zu erstellen, bedeutet einfach, die Leistung der App zu reduzieren. –

5
-(BOOL)shouldInvalidateLayoutForBoundsChange { 
     return YES; 
} 

Verursacht das Layout prepareLayout() jedes Mal, es rollt, zu tun, die etwas schwerer Rechenmittel in Vorbereitung wird zu einer laggy Praxis führen, so eine mögliche Richtung zu lösen, dies zu überprüfen, ist das, was wirklich ist viel Zeit nehmen. Eine Möglichkeit ist, was drin ist

for (NSInteger section = 0; section < sectionCount; section++) 
{ 
    // generate attributes ... 
} 

, um Attribute für das Layout zu generieren. Jedes Mal, wenn es scrollt, erscheint jedes Mal, wenn diese Generalisierung erneut ausgeführt wird, so dass es auf die Schriftrolle wirkt, nervös und unbeholfen. Also, um dieses Problem zu lösen, oder zumindest zu sortieren, dass dies nicht das Problem ist, schlage ich vor, eine Flag in diesem Layout-Algorithmus, sagen wir isScrolling, für die Situation, wo das Layout vorbereiten muss stehen. Jedes Mal in prepareLayout() überprüfen Sie die Flagge, wenn es YES ist, dann wissen wir, es gibt keine Notwendigkeit, für Schleife zu tun, um alle Attribute zu regenerieren, die bereits seit der ersten Initialisierung des Layouts vorhanden sind.

+1

Aus diesem Grund sollten Sie in sinetInvalidateLayoutForBoundsChange eine Logik implementieren, um festzustellen, ob das Layout ungültig gemacht werden muss. Rückkehr JA ist böse. – Andy

9

In benutzerdefinierten Flow Layout mache ich das und es scheint zu helfen:

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds { 
    return !(CGSizeEqualToSize(newBounds.size, self.collectionView.frame.size)); 
} 
+1

Funktioniert das? An dieser Stelle scheint es, dass die Sammlungsansicht bereits die neue Größe hat und somit immer der Größe von newBounds entspricht. – mattyohe

Verwandte Themen