Frage UICollectionView flowLayout umschließt Zellen nicht korrekt (iOS)


Ich habe ein UICollectionView mit einem FLowLayout. Es wird funktionieren, wie ich die meiste Zeit erwarte, aber hin und wieder wickelt sich eine der Zellen nicht richtig ein. Zum Beispiel die Zelle, die in der ersten "Spalte" der dritten Zeile enthalten sein sollte, wenn sie tatsächlich in der zweiten Zeile folgt, und es gibt nur eine leere Stelle, wo sie sein sollte (siehe Diagramm unten). Alles, was du von dieser Rouge-Zelle sehen kannst, ist die linke Seite (der Rest ist abgeschnitten) und der Platz, an dem es sein sollte, ist leer.

Dies geschieht nicht konsequent; es ist nicht immer dieselbe Reihe. Sobald es passiert ist, kann ich nach oben und dann zurück scrollen und die Zelle wird sich selbst repariert haben. Oder, wenn ich die Zelle drücke (die mich durch einen Druck zur nächsten Ansicht bringt) und dann zurückspringe, sehe ich die Zelle in der falschen Position und dann springt sie zur richtigen Position.

Die Scroll-Geschwindigkeit scheint es einfacher zu machen, das Problem zu reproduzieren. Wenn ich langsam scrolle, kann ich die Zelle immer noch in der falschen Position sehen, aber dann springt sie sofort in die richtige Position.

Das Problem begann, als ich die Abschnittseinfügungen hinzufügte. Zuvor hatte ich die Zellen fast bündig gegen die Sammlungsgrenzen (wenig oder keine Einsätze) und ich habe das Problem nicht bemerkt. Aber das bedeutete, dass die rechte und linke Seite der Sammlungsansicht leer war. Dh, konnte nicht scrollen. Außerdem war die Bildlaufleiste nicht rechtsbündig.

Ich kann das Problem sowohl auf Simulator als auch auf einem iPad 3 lösen.

Ich schätze, dass das Problem wegen der linken und rechten Abschnittseinsätze passiert ... Aber wenn der Wert falsch ist, würde ich erwarten, dass das Verhalten konsistent ist. Ich frage mich, ob das ein Fehler mit Apple sein könnte? Oder vielleicht liegt das an einem Aufbau der Einsätze oder etwas Ähnliches ...

Irgendwelche Lösungen / Workarounds werden geschätzt.

Illustration of problem and settings


Nachverfolgen: Ich habe benutzt diese Antwort unten von Nick für über 6 Monate  12 Monate 2 Jahre jetzt ohne Probleme (für den Fall, dass die Leute sich fragen, ob es Löcher in dieser Antwort gibt - ich habe noch keine gefunden). Gut gemacht, Nick.


75
2017-10-17 04:18


Ursprung


Antworten:


Es gibt einen Fehler in der Implementierung von layoutAttributesForElementsInRect durch UICollectionViewFlowLayout, die dazu führt, dass in bestimmten Fällen, in denen Abschnittseinfügungen enthalten sind, ZWEI Attributobjekte für eine einzelne Zelle zurückgegeben werden. Eines der zurückgegebenen Attributobjekte ist ungültig (außerhalb der Grenzen der Sammlungsansicht) und das andere ist gültig. Im Folgenden finden Sie eine Unterklasse von UICollectionViewFlowLayout, die das Problem dadurch behebt, dass Zellen außerhalb der Grenzen der Sammlungsansicht ausgeschlossen werden.

// NDCollectionViewFlowLayout.h
@interface NDCollectionViewFlowLayout : UICollectionViewFlowLayout
@end

// NDCollectionViewFlowLayout.m
#import "NDCollectionViewFlowLayout.h"
@implementation NDCollectionViewFlowLayout
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
  NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
  NSMutableArray *newAttributes = [NSMutableArray arrayWithCapacity:attributes.count];
  for (UICollectionViewLayoutAttributes *attribute in attributes) {
    if ((attribute.frame.origin.x + attribute.frame.size.width <= self.collectionViewContentSize.width) &&
        (attribute.frame.origin.y + attribute.frame.size.height <= self.collectionViewContentSize.height)) {
      [newAttributes addObject:attribute];
    }
  }
  return newAttributes;
}
@end

Sehen Dies.

Andere Antworten schlagen vor, YES von shouldInvalidateLayoutForBoundsChange zurückzugeben, aber dies verursacht unnötige Neuberechnung und löst das Problem nicht einmal vollständig.

Meine Lösung löst den Fehler vollständig und sollte keine Probleme verursachen, wenn Apple die Ursache beseitigt.


91
2017-11-14 23:55



Ich entdeckte ähnliche Probleme in meiner iPhone-Anwendung. Die Suche im Apple Dev Forum brachte mir diese passende Lösung, die in meinem Fall funktionierte und wahrscheinlich auch in Ihrem Fall sein wird:

Unterklasse UICollectionViewFlowLayout und überschreiben shouldInvalidateLayoutForBoundsChange zurückgeben YES.

//.h
@interface MainLayout : UICollectionViewFlowLayout
@end

und

//.m
#import "MainLayout.h"
@implementation MainLayout
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
    return YES;
}
@end

7
2017-10-31 20:06



Fügen Sie dies in den ViewController ein, der die Sammlungsansicht besitzt

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    [self.collectionView.collectionViewLayout invalidateLayout];
}

7
2018-06-03 08:48



Eine schnelle Version von Nick Snyders Antwort:

class NDCollectionViewFlowLayout : UICollectionViewFlowLayout {
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let attributes = super.layoutAttributesForElements(in: rect)
        let contentSize = collectionViewContentSize
        return attributes?.filter { $0.frame.maxX <= contentSize.width && $0.frame.maxY < contentSize.height }
    }
}

7
2017-07-03 12:06



Ich hatte dieses Problem auch für ein grundlegendes Gridview-Layout mit Einfügungen für Ränder. Das begrenzte Debugging, das ich für jetzt durchgeführt habe, wird implementiert - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect in meiner UICollectionViewFlowLayout-Unterklasse und durch Protokollieren, was die Superklassenimplementierung zurückgibt, was das Problem deutlich zeigt.

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray *attrsList = [super layoutAttributesForElementsInRect:rect];

    for (UICollectionViewLayoutAttributes *attrs in attrsList) {
        NSLog(@"%f %f", attrs.frame.origin.x, attrs.frame.origin.y);
    }

    return attrsList;
}

Durch die Implementierung - (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath Ich kann auch sehen, dass es die falschen Werte für itemIndexPath.item == 30 zurückgibt, was Faktor 10 der Anzahl meiner Zellen pro Zeile ist, nicht sicher, ob das relevant ist.

- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath {
    UICollectionViewLayoutAttributes *attrs = [super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath];

    NSLog(@"initialAttrs: %f %f atIndexPath: %d", attrs.frame.origin.x, attrs.frame.origin.y, itemIndexPath.item);

    return attrs;
}

Da die Zeit für mehr Debugging zu kurz ist, wurde die Sammelzugriffsbreite mit der Problemumgehung, die ich jetzt gemacht habe, um einen Betrag verringert, der dem linken und rechten Rand entspricht. Ich habe einen Header, der immer noch die volle Breite benötigt, also habe ich clipsToBounds = NO in meiner Collectionview gesetzt und dann auch die linken und rechten Einfügungen entfernt, scheint zu funktionieren. Damit die Header-Ansicht dann beibehalten wird, müssen Sie Frame-Verschiebung und Dimensionierung in den Layout-Methoden implementieren, die mit der Rückgabe von layoutAttributes für die Header-Ansicht beauftragt werden.


5
2017-10-17 09:28



Ich habe Apple einen Fehlerbericht hinzugefügt. Was für mich funktioniert, ist, den unteren Abschnitt auf einen Wert unterhalb des oberen Einschubs zu setzen.


4
2017-11-08 13:26



Ich hatte das gleiche Problem mit dem Entfernen von Zellen auf dem iPhone mit einem UICollectionViewFlowLayout und so war ich froh, deinen Posten zu finden. Ich weiß, dass Sie das Problem auf einem iPad haben, aber ich poste das, weil ich denke, dass es ein allgemeines Problem mit dem ist UICollectionView. Also hier ist was ich herausgefunden habe.

Ich kann bestätigen, dass die sectionInset ist relevant für dieses Problem. Außerdem headerReferenceSize hat auch Einfluss darauf, ob eine Zelle deplaciert ist oder nicht. (Dies ist sinnvoll, da es zur Berechnung des Ursprungs benötigt wird.)

Leider müssen sogar unterschiedliche Bildschirmgrößen berücksichtigt werden. Beim Spielen mit den Werten für diese beiden Eigenschaften habe ich festgestellt, dass eine bestimmte Konfiguration entweder auf (3,5 "und 4"), auf keiner oder nur auf einer der Bildschirmgrößen funktioniert hat. Normalerweise keiner von ihnen. (Dies macht auch Sinn, da die Grenzen der UICollectionView Veränderungen, daher habe ich keine Disparität zwischen Netzhaut und Nicht-Netzhaut festgestellt.)

Ich landete die Einstellung sectionInset und headerReferenceSize abhängig von der Bildschirmgröße. Ich habe etwa 50 Kombinationen ausprobiert, bis ich Werte gefunden habe, unter denen das Problem nicht mehr auftrat und das Layout visuell akzeptabel war. Es ist sehr schwierig, Werte zu finden, die auf beiden Bildschirmgrößen funktionieren.

Zusammenfassend kann ich Ihnen nur empfehlen, mit den Werten herumzuspielen, diese auf verschiedenen Bildschirmgrößen zu überprüfen und zu hoffen, dass Apple dieses Problem beheben wird.


3
2017-10-29 17:50