webentwicklung-frage-antwort-db.com.de

das Verhalten von UICollectionViewFlowLayout ist nicht definiert, da die Zellenbreite größer als die Breite von collectionView ist

2015-08-18 16: 07: 51.523 Beispiel [16070: 269647] das Verhalten von UICollectionViewFlowLayout ist nicht definiert, da 2015-08-18 16: 07: 51,523
Beispiel [16070: 269647] Die Artikelbreite muss kleiner als .__ sein. die Breite der UICollectionView minus den verbleibenden Abschnitten und rechte Werte, minus die linken und rechten Werte des Inhalts.
2015-08-18 16: 07: 51.524 Beispiel [16070: 269647] Die relevanten Die UICollectionViewFlowLayout-Instanz ist und ist mit dieser verbunden. Animationen = { Position =; bounds.Origin =; bounds.size =; }; Schicht =; contentOffset: {0, 0}; contentSize: {1024, 770}> Layout der Sammlungsansicht: . 2015-08-18 16: 07: 51.524 Beispiel [16070: 269647] Machen Sie eine Symbolik Haltepunkt bei UICollectionViewFlowLayoutBreakForInvalidSizes, der abzufangen ist dies im Debugger.

Das bekomme ich, was ich tue 

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
        return CGSizeMake(self.collectionView!.frame.size.width - 20, 66)
    }

wenn ich von Querformat zu Hochformat wechsle, zeigt die Konsole diese Fehlermeldung nur in iOS 9 an. Weiß jemand, was passiert und ob es ein Update gibt?

 Screenshot

34

Dies geschieht, wenn die Ansicht Ihrer Sammlung weniger breit ist (z. B. vom Querformat in den Hochformatmodus) und die Zelle zu groß wird.

Warum wird die Zelle zu groß, da das Layout der Sammlungsansicht aufgerufen werden soll und eine geeignete Größe zurückgeben soll?

collectionView (collectionView: UICollectionView, Layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath)

Aktualisieren Sie Swift 4

@objc überschreibt func collectionView (_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {...}

Das liegt daran, dass diese Funktion nicht aufgerufen wird oder zumindest nicht sofort.

Was passiert, ist, dass die Layout-Unterklasse des Sammlungsansichtsflusses die Funktion shouldInvalidateLayoutForBoundsChange nicht überschreibt, die standardmäßig returns false ist.

Wenn diese Methode "false" zurückgibt, versucht die Auflistungsansicht zunächst, die aktuelle Zellengröße festzulegen, erkennt ein Problem (das die Warnung protokolliert) und ruft dann das Flusslayout auf, um die Größe der Zelle zu ändern.

Das bedeutet 2 Dinge:

1 - Die Warnung an sich ist nicht schädlich

2 - Sie können es entfernen, indem Sie einfach die Funktion shouldInvalidateLayoutForBoundsChange überschreiben, um true .. zu übergeben. In diesem Fall wird das Flusslayout immer aufgerufen, wenn sich die Grenzen der Auflistungsansicht ändern.

27
AirXygène

Dies ist darauf zurückzuführen, dass die Breite Ihrer Sammlungsansicht-Zelle nach der Drehung größer als die Breite der Sammlungsansicht ist. Angenommen, Sie haben einen Bildschirm mit 1024 x 768 Pixeln und Ihre Sammlungsansicht füllt den Bildschirm aus. Wenn Ihr Gerät Querformat hat, wird die Breite Ihrer Zelle self.collectionView.frame.size sein .width - 20 = 1004 und größer als die Breite Ihrer Sammlungsansicht im Hochformat = 768. Der Debugger sagt, dass "die Breite des Elements geringer sein muss als die Breite der UICollectionView, abzüglich der linken und rechten Werte der Abschnittsinsets, abzüglich der verbleibenden Inhaltsinsets und richtige Werte ".

5

Ich verwende eine Unterklasse von UICollectionViewFlowLayout und benutze ihre itemSize-Eigenschaft, um die Zellengröße anzugeben (anstelle der collectionView:sizeForItemAtIndexPath:-Delegat-Methode). Jedes Mal, wenn ich den Bildschirm um eine kleinere Breite (z. B. Landschaft -> Porträt) drehe, bekomme ich diese große Warnung in Frage.

Ich konnte es durch zwei Schritte beheben.

Schritt 1: Verschieben Sie in der prepareLayout()-Funktion der UICollectionViewFlowLayout-Unterklasse super.prepareLayout() nach nach, wo self.itemSize eingestellt ist. Ich denke, dass die Superklasse den korrekten itemSize-Wert verwendet.

import UIKit

extension UICollectionViewFlowLayout {

  var collectionViewWidthWithoutInsets: CGFloat {
    get {
      guard let collectionView = self.collectionView else { return 0 }
      let collectionViewSize = collectionView.bounds.size
      let widthWithoutInsets = collectionViewSize.width
        - self.sectionInset.left - self.sectionInset.right
        - collectionView.contentInset.left - collectionView.contentInset.right
      return widthWithoutInsets
    }
  }

}

class StickyHeaderCollectionViewLayout: UICollectionViewFlowLayout {

  // MARK: - Variables
  let cellAspectRatio: CGFloat = 3/1

  // MARK: - Layout
  override func prepareLayout() {
    self.scrollDirection = .Vertical
    self.minimumInteritemSpacing = 1
    self.minimumLineSpacing = 1
    self.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: self.minimumLineSpacing, right: 0)
    let collectionViewWidth = self.collectionView?.bounds.size.width ?? 0
    self.headerReferenceSize = CGSize(width: collectionViewWidth, height: 40)

    // cell size
    let itemWidth = collectionViewWidthWithoutInsets
    self.itemSize = CGSize(width: itemWidth, height: itemWidth/cellAspectRatio)

    // Note: call super last if we set itemSize
    super.prepareLayout()
  }

  // ...

}

Beachten Sie, dass die obige Änderung die Layoutgröße ändert, wenn sich der Bildschirm dreht. Hier kommt Schritt 2 ins Spiel.

Schritt 2: Fügen Sie dies in den view Controller ein, der die Sammlungsansicht enthält.

  override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
    collectionView.collectionViewLayout.invalidateLayout()
  }

Jetzt ist die Warnung weg :)


Einige Notizen:

  1. Stellen Sie sicher, dass Sie der collectionView Einschränkungen hinzufügen und nicht collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight] verwenden.

  2. Stellen Sie sicher, dass Sie nicht invalidateLayout in viewWillTransitionToSize() aufrufen, da die Breite einer Edge-to-Edge-Zelle in Querformat größer ist als die Bildbreite der Sammlungsansicht im Hochformat. Siehe unten Referenzen.

Verweise

5
Hlung

Ich habe dieses Problem mit safeAreaLayoutGuide gelöst.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    return CGSize(width: (view.safeAreaLayoutGuide.layoutFrame.width), height: 80);
}

sie müssen diese Funktion auch überschreiben, um den Hoch- und Querformatmodus richtig zu unterstützen.

 override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    collectionView?.collectionViewLayout.invalidateLayout();
}
4
Luis Santiago

Sie können im Debugger prüfen, ob collectionView.contentOffset in meinem Fall negativ ist, es ändert sich jedoch von (0,0) in (0,-40). Sie können dieses Problem mit dieser Methode lösen 

if ([self respondsToSelector:@selector(setAutomaticallyAdjustsScrollViewInsets:)]) {
   self.automaticallyAdjustsScrollViewInsets = NO;
}
3
Anshul

Nach einigen Experimenten scheint dies auch darauf zurückzuführen zu sein, wie Sie Ihre collectionView gestalten. 

Das Tl; dr ist: AutoLayout verwenden, nicht autoresizingMask.

Für den Kern des Problems verwenden die besten Lösungen, die ich gefunden habe, um die Orientierungsänderung zu handhaben, den folgenden Code, der alles Sinn macht:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    coordinator.animate(alongsideTransition: { (context) in
        self.collectionView.collectionViewLayout.invalidateLayout()
    }, completion: nil)
}

Allerdings, es gibt immer noch Situationen, in denen Sie die Warnung zur Elementgröße erhalten können. Wenn ich mich im Querformat auf einer Registerkarte befinde, dann zu einer anderen Registerkarte wechseln, zum Hochformat drehen und dann zur vorherigen Registerkarte zurückkehren. Ich habe versucht, das Layout in willAppear ungültig zu machen, willLayout, alle üblichen Verdächtigen, aber kein Glück. Selbst wenn Sie invalidateLayoutvorsuper.willAppear() aufrufen, erhalten Sie immer noch die Warnung. 

Letztendlich ist dieses Problem an die Größe der Aktualisierung der collectionView-Begrenzungen gebunden, bevor der Delegat nach der Elementgröße gefragt wird.

Deshalb habe ich versucht, AutoLayout anstelle von collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight] zu verwenden, und damit das Problem gelöst! (Ich benutze SnapKit, um AutoLayout auszuführen, damit ich meine Haare nicht ständig herausziehe). Sie müssen auch nur invalidateLayout in viewWillTransition (ohne den coordinator.animate, also genau wie in Ihrem Beispiel) und auch invalidateLayout am Ende von viewWillAppear(:). Das scheint alle Situationen abzudecken.

Ich weiß nicht, ob Sie Autoresizing verwenden oder nicht - es wäre interessant zu wissen, ob meine Theorie/Lösung für alle geeignet ist.

2
Loz

Ich habe auch gesehen, wie dies vorkam, wenn wir den geschätztenElementwert auf automaticSize setzen und die berechnete Größe der Zellen weniger als 50x50 beträgt (Hinweis: Diese Antwort war zum Zeitpunkt von iOS 12 gültig. Möglicherweise haben spätere Versionen sie behoben).

d. h., wenn Sie die Unterstützung für Zellen zur Größenänderung deklarieren, indem Sie die geschätzte Elementgröße auf die Konstante automaticSize setzen:

flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

und Ihre Zellen sind tatsächlich kleiner als 50x50 Punkte, dann druckt UIKit diese Warnungen aus. FWIW, die Zellen haben schließlich eine angemessene Größe und die Warnungen scheinen harmlos zu sein.

Ein fix Umgehung besteht darin, die Konstante durch eine geschätzte 1x1-Elementgröße zu ersetzen:

flowLayout.estimatedItemSize = CGSize(width: 1, height: 1)

Dies wirkt sich nicht auf die Selbstgrößenunterstützung aus, da in der Dokumentation für estimatedItemSize Folgendes erwähnt wird:

Wenn Sie diesen Wert auf einen anderen Wert setzen, fragt die Auflistungsansicht jede Zelle nach ihrer tatsächlichen Größe ab. Verwenden Sie dazu die Methode PreferredLayoutAttributesFitting (_ :) der Zelle. 

Es kann jedoch auch Auswirkungen auf die Leistung haben.

2
Manav

Das funktioniert für mich:

Das Layout bei Rotation ungültig machen:

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    collectionView.collectionViewLayout.invalidateLayout()
}

Sie haben eine separate Funktion zur Berechnung der verfügbaren Breite:

Hinweis: Berücksichtigen Sie dabei sowohl die Einfügung der Abschnitte als auch die Einfügung der Inhalte.

// MARK: - Helpers

func calculateCellWidth(for collectionView: UICollectionView, section: Int) -> CGFloat {
    var width = collectionView.frame.width
    let contentInset = collectionView.contentInset
    width = width - contentInset.left - contentInset.right

    // Uncomment the following two lines if you're adjusting section insets
    // let sectionInset = self.collectionView(collectionView, layout: collectionView.collectionViewLayout, insetForSectionAt: section)
    // width = width - sectionInset.left - sectionInset.right

    // Uncomment if you are using the sectionInset property on flow layouts
    // let sectionInset = (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.sectionInset ?? UIEdgeInsets.zero
    // width = width - sectionInset.left - sectionInset.right

    return width
}

Und dann natürlich die Artikelgröße zurückgeben:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width: calculateCellWidth(for: collectionView, section: indexPath.section), height: 60)
}

Ich denke, es ist wichtig zu wissen, dass dies funktioniert, da das Ungültigmachen eines Layouts nicht sofort eine Neuberechnung der Zellengröße auslöst, sondern während des nächsten Layoutaktualisierungszyklus. Das bedeutet, dass nach dem Aufruf des Elementgrößen-Callbacks die Auflistungsansicht den richtigen Frame aufweist, sodass eine genaue Größenanpassung möglich ist.

Voila!

2
josh-fuggle

Ich hatte ein ähnliches Problem.

In meinem Fall hatte ich eine Sammlungsansicht und als Sie auf eine der Zellen tippten, wurde ein Popover mit einer UITextField geöffnet, um das Element zu bearbeiten. Nachdem der Popover verschwunden war, wurde der self.collectionView.contentInset.bottom auf 55 (ursprünglich 0) gesetzt.

Um mein Problem zu beheben, setze ich contentInset manuell auf UIEdgeInsetsZero, nachdem die Popover-Ansicht nicht mehr angezeigt wird.

contextual prediction bar

Das ursprüngliche Problem scheint sich auf die kontextabhängige Vorhersageleiste zu beziehen, die auf der Tastatur angezeigt wird. Wenn die Tastatur ausgeblendet ist, wird die Leiste ausgeblendet, der contentInset.bottom-Wert wird jedoch nicht auf den ursprünglichen Wert zurückgesetzt.

Da sich Ihr Problem scheinbar auf die Breite und nicht auf die Höhe der Zelle bezieht, prüfen Sie, ob die Werte für contentInset oder layout.sectionInset mit den von Ihnen festgelegten Werten übereinstimmen.

1
Marius

Ich hatte das gleiche Problem. Ich habe das behoben, indem ich meine Sammlungsansicht von den Einschränkungen löschte und sie in Storyboard zurücksetzte. 

1
Dayna

Ich fand, dass dies fürUICollectionViewControllerziemlich gut funktionierte und richtig animierte:

- (void)viewWillTransitionToSize:(CGSize)size
       withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    // Ensure the layout is within the allowed size before animating below, or 
    //  `UICollectionViewFlowLayoutBreakForInvalidSizes` breakpoint will trigger
    //  and the logging will occur.

    if ([self.collectionView.collectionViewLayout isKindOfClass:[YourLayoutSubclass class]]) {
        [(YourLayoutSubclass *)self.collectionView.collectionViewLayout updateForWidth:size.width];
    }

    // Then, animate alongside the transition, as you would:

    [coordinator animateAlongsideTransition:^
        (id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
            [self.collectionView.collectionViewLayout invalidateLayout];
        }
                                 completion:nil];

    [super viewWillTransitionToSize:size
          withTransitionCoordinator:coordinator];
}

///////////////

@implementation YourLayoutSubclass

- (void)prepareLayout {
    [super prepareLayout];

    [self updateForWidth:self.collectionView.bounds.size.width];
}

- (void)updateForWidth:(CGFloat)width {
    // Update layout as you wish...
}

@end

... Weitere Informationen finden Sie oben in den Inline-Code-Kommentaren. ????????

0
Ben Guild

Ich könnte es lösen, indem ich super.prepare() am Ende von override func prepare() { } setze

0
Wasim