2017-04-24 5 views
0

Ich bin ein schneller/iOS-Anfänger und ich fange an zu denken, dass ich Everest als meine erste Aufgabe angenommen habe. In jedem Fall hat meine App eine wöchentliche Wochenkalenderansicht. Ich benutze eine benutzerdefinierte UICollectionViewLayout. Wenn die App startet, sieht alles gut aus und ich kann die 24-Stunden-Anzeige und die ganzen Tage durchblättern (siehe erstes Bild).UICollectionViewLayout "friert ein"

Layout on start-up

Wenn der Benutzer das Ende der vorgegebenen Anzahl von Zellen (Tage) mehr Zellen erreicht werden, an die Datenquelle hinzugefügt. Ich kann glücklich fortlaufend scrollen und der Code fügt mehr Zellen nach Bedarf hinzu.

Das Problem:

Als ich durch die Tage zu stoppen Scrollen nach allen zusätzlichen Daten (Tage) auf die Datenquelle hinzugefügt wurden, und dann neu starten Scrollen das Layout in einem einzigen Block gefriert und um nur schwimmt (siehe zweites Bild).

'Frozen' layout

Die richtige Platzierung (Pinning) von Zellen an der Spitze der Ansicht funktioniert nicht mehr (als nur die oben genannten gefrorenen Block von Zellen) und keine neuen Zellen angezeigt werden.

Kurioserweise, nach dem Einfrieren des Layouts weiterhin das Debugging von der UICollectionViewLayout abmelden die Zellen an der richtigen Stelle platzieren, obwohl es scheint, die UICollectionViewController hat gerade aufgehört zu hören?

im Terminal Gedruckt für die 'Datum' Zelle: Cell attributes: <UICollectionViewLayoutAttributes: 0x6080003edf00> index path: (<NSIndexPath: 0x60800003a760> {length = 2, path = 2 - 0}); frame = (1033 235; 47 50); zIndex = 1024; 

Aus der View-Hierarchie debug: (UICollectionView): <UICollectionView: 0x7feb7302d000; frame = (0 0; 667 375); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x600000051490>; layer = <CALayer: 0x6000000329a0>; contentOffset: {1033, 235}; contentSize: {2651, 910}> collection view layout: <V3.CDCLayout: 0x7feb7140f1a0>

Aus der View-Hierarchie debug für die Zelle 'Datums': <V3.CDCMonthCell: 0x7feb7410a690; baseClass = UICollectionViewCell; frame = (1201.5 361; 47 50); clipsToBounds = YES; opaque = NO; layer = <CALayer: 0x608000035180>>

[UPDATE ]

Hallo Rob - Ich habe versucht, meinen Code zu entfernen und folgte dann ein paar Tutorials, die alle mit dem gleichen Problem endete, wenn ich meine Datenquelle (Singleton) - Es gibt also einen heißen Verdächtigen (!), aber ich kann es mir nicht vorstellen. Der folgende Code basiert auf einem Lernbeispiel, weist jedoch das gleiche Problem auf.

CollectionViewController:

private let reuseIdentifier = "cCell" 

class CustomCollectionViewController: UICollectionViewController { 

    let localItems = Items.items.getItems() 


    override func viewDidLoad() { 
     super.viewDidLoad() 
    } 

    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 
    } 

    override func numberOfSections(in collectionView: UICollectionView) -> Int { 
     return 6 } 


    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 
     return Items.items.getItemCount() 
    } 

    override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { 

     if (Items.items.getItemCount() - indexPath.row) == 2 { 
      Items.items.addItems() 
      collectionView.reloadData() 
      collectionView.collectionViewLayout.invalidateLayout() 
     } 
    } 

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> CustomCollectionViewCell { 
     let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! CustomCollectionViewCell 

     cell.textLabel.text = "SEC=\(indexPath.section),IXDI=\(indexPath.item)" 

     return cell 
    } 
} 

Layout:

class CustomCollectionViewLayout: UICollectionViewLayout { 

    let CELL_HEIGHT = 145.0 
    let CELL_WIDTH = 145.0 
    let STATUS_BAR = UIApplication.shared.statusBarFrame.height 
    var cellAttrsDictionary = Dictionary<IndexPath, UICollectionViewLayoutAttributes>() 
    var contentSize = CGSize.zero 

    override var collectionViewContentSize : CGSize { 
     return contentSize 
    } 

    override func invalidateLayout() { 
     super.invalidateLayout() 
     cellAttrsDictionary.removeAll() 
    } 

    override func prepare() { 

     if (collectionView?.numberOfSections)! > 0 { 
      for section in 0...collectionView!.numberOfSections-1 { 

       if (collectionView?.numberOfItems(inSection: section))! > 0 { 
        for item in 0...collectionView!.numberOfItems(inSection: section)-1 { 

         let cellIndex = IndexPath(item: item, section: section) 
         let xPos = Double(item) * CELL_WIDTH 
         let yPos = Double(section) * CELL_HEIGHT 

         let cellAttributes = UICollectionViewLayoutAttributes(forCellWith: cellIndex) 
         cellAttributes.frame = CGRect(x: xPos, y: yPos, width: CELL_WIDTH, height: CELL_HEIGHT) 

         if section == 0 && item == 0 { 
          cellAttributes.zIndex = 4 
         } else if section == 0 { 
          cellAttributes.zIndex = 3 
         } else if item == 0 { 
          cellAttributes.zIndex = 2 
         } else { 
          cellAttributes.zIndex = 1 
         } 

         cellAttrsDictionary[cellIndex] = cellAttributes 
        } 
       } 
      } 
     } 

     let contentWidth = Double(collectionView!.numberOfItems(inSection: 0)) * CELL_WIDTH 
     let contentHeight = Double(collectionView!.numberOfSections) * CELL_HEIGHT 
     self.contentSize = CGSize(width: contentWidth, height: contentHeight) 
    } 

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 
     var attributesInRect = [UICollectionViewLayoutAttributes]() 

     for cellAttributes in cellAttrsDictionary.values { 
      if rect.intersects(cellAttributes.frame) { 
       attributesInRect.append(cellAttributes) 
      } 
     } 
     return attributesInRect 
    } 

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { 
     return cellAttrsDictionary[indexPath]! 
    } 

    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { 
     return true 
    } 
} 

Positionen/Daten-Quelle/Singletons:

class Items { 

    static var items = Items() // Singleton 

    var classItems = [Int]() 

    init() { 
     addItems() 
    } 

    func getItemCount() -> Int { 
     return classItems.count 
    } 

    func getItems() -> [Int] { 
     return classItems 
    } 

    func addItems() { 
     for i in 1 ... 7 { 
      classItems.append(i) 
     } 
    } 

} 
+1

Es wird schwer sein, dies ohne Code zu diagnostizieren. Ich würde Sie ein [minimal, vollständiges, überprüfbares Beispiel (MCVE)] (http://stackoverflow.com/help/mcve) des Problems vorschlagen erstellen und dann schreiben Sie das Bild und den Code für die für diesen MCVE. Aber es gibt hier nicht genug, um zu verstehen, was schief gelaufen ist und wir wollen definitiv nicht den ganzen Code sehen, der diese Bilder produziert hat. Wir brauchen MCVE mit Code. – Rob

+0

Danke, dass du dir Rob angeschaut hast. Guter Vorschlag auf dem MCVE. Lass mich das tun. – smarts

+0

BTW, wenn Sie Ihre Sammlung Ansicht nicht erscheint auch die Aktualisierung werden, auch wenn Sie Beweise haben, dass die App nicht gesichert ist, würde ich ein Auge von einem Hintergrund-Thread für jegliche versehentliche UI-Updates aus (entweder etwas, das Sie manuell eine versandt Hintergrundwarteschlange oder etwas in einem 'URLSession'-Vervollständigungshandler, der in einer Hintergrundwarteschlange ausgeführt wird. Stellen Sie sicher, dass Sie keine 'reloadData' oder ähnliche Aufrufe für Hintergrundthreads haben. – Rob

Antwort

0

In meinen Tests, schien das Nachladen der Datenquelle in willDisplay um die Wurzel des Problems zu sein. Durch DispatchQueue.main.async { ... } schienen die Probleme zu verschwinden.

private var lastReloadedRow = 0 

override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { 
    if (Items.items.getItemCount() - indexPath.row) == 2 && lastReloadedRow < indexPath.row { 
     lastReloadedRow = indexPath.row 
     DispatchQueue.main.async { 
      Items.items.addItems() 
      collectionView.reloadData() 
     } 
    } 
} 

Hinweis, habe ich einen Zustand Eigenschaft, um zu verfolgen, ob wir schon geladen, weil die Art und Weise es war, es war unnötig mehrmals neu zu laden.


Der Vollständigkeit halber ist hier meine vollständige Implementierung Ihres benutzerdefinierten Layouts.Ich habe ein paar nicht verwandte Dinge gemacht:

So:

class CustomCollectionViewLayout: UICollectionViewLayout { 

    let CELL_HEIGHT = 145.0 
    let CELL_WIDTH = 145.0 
    let STATUS_BAR = UIApplication.shared.statusBarFrame.height 
    var cellAttrsDictionary = Dictionary<IndexPath, UICollectionViewLayoutAttributes>() 
    var contentSize = CGSize.zero 

    override var collectionViewContentSize : CGSize { 
     return contentSize 
    } 

    override func invalidateLayout() { 
     super.invalidateLayout() 
     cellAttrsDictionary.removeAll() 
    } 

    override func prepare() { 
     if collectionView!.numberOfSections > 0 { 
      for section in 0 ..< collectionView!.numberOfSections { 
       if collectionView!.numberOfItems(inSection: section) > 0 { 
        for item in 0 ..< collectionView!.numberOfItems(inSection: section) { 
         let cellIndex = IndexPath(item: item, section: section) 
         let xPos = Double(item) * CELL_WIDTH 
         let yPos = Double(section) * CELL_HEIGHT 

         let cellAttributes = UICollectionViewLayoutAttributes(forCellWith: cellIndex) 
         cellAttributes.frame = CGRect(x: xPos, y: yPos, width: CELL_WIDTH, height: CELL_HEIGHT) 

         cellAttrsDictionary[cellIndex] = cellAttributes 
        } 
       } 
      } 
     } 

     let contentWidth = Double(collectionView!.numberOfItems(inSection: 0)) * CELL_WIDTH 
     let contentHeight = Double(collectionView!.numberOfSections) * CELL_HEIGHT 
     contentSize = CGSize(width: contentWidth, height: contentHeight) 
    } 

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 
     return cellAttrsDictionary.values.filter { $0.frame.intersects(rect) } 
    } 

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { 
     return cellAttrsDictionary[indexPath]! 
    } 

    // override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { 
    //  return true 
    // } 
} 
+0

Protokoll scheint zu verhindern danke; aber hey - Kredit, wo es fällig ist - große Arbeit. Gute Aufnahme bei der zusätzlichen Stimmung. – smarts