UICollectionView reloadData funktioniert in iOS 7 nicht ordnungsgemäß

93

Ich habe meine Apps so aktualisiert, dass sie unter iOS 7 laufen, was größtenteils reibungslos funktioniert. Ich habe in mehr als einer App festgestellt, dass sich die reloadDataMethode von a UICollectionViewControllernicht mehr so ​​verhält wie früher.

Ich werde das laden UICollectionViewController, das UICollectionViewmit einigen Daten wie gewohnt füllen. Dies funktioniert beim ersten Mal hervorragend. Wenn ich jedoch neue Daten anfordere (füllen Sie die aus UICollectionViewDataSource) und dann aufrufe reloadData, wird die Datenquelle nach numberOfItemsInSectionund abgefragt numberOfSectionsInCollectionView, aber es scheint nicht cellForItemAtIndexPathdie richtige Anzahl von Malen aufzurufen .

Wenn ich den Code so ändere, dass nur ein Abschnitt neu geladen wird, funktioniert er ordnungsgemäß. Das ist für mich kein Problem, diese zu ändern, aber ich denke nicht, dass ich das muss. reloadDatasollte alle sichtbaren Zellen gemäß der Dokumentation neu laden.

Hat das noch jemand gesehen?

VaporwareWolf
quelle
5
Das gleiche hier, es ist in iOS7GM, hat vorher gut funktioniert. Mir ist aufgefallen, dass ein Aufruf reloadDatanach viewDidAppear das Problem zu lösen scheint, seine schreckliche Problemumgehung und eine Lösung erforderlich ist. Ich hoffe, hier hilft jemand.
JasonIM
1
Das gleiche Problem haben. Code, der in iOS6 einwandfrei funktioniert. ruft jetzt nicht cellforitematindexpath auf, obwohl die richtige Anzahl von Zellen zurückgegeben wird
Avner Barr
Wurde dies in einer Version nach 7.0 behoben?
William Jockusch
Ich stehe immer noch vor Problemen im Zusammenhang mit diesem Problem.
Anil
Ähnliches Problem nach dem spontanen Ändern von [collectionView setFrame]; Stellt immer eine Zelle in die Warteschlange und das ist es, unabhängig von der Nummer in der Datenquelle. Versuchte alles hier und mehr und kann es nicht umgehen.
RegularExpression

Antworten:

72

Erzwinge dies auf dem Haupt-Thread:

dispatch_async(dispatch_get_main_queue(), ^ {
    [self.collectionView reloadData];
});
Shaunti Fondrisi
quelle
1
Ich bin mir nicht sicher, ob ich mehr erklären kann. Nach dem Suchen, Erforschen, Testen und Prüfen. Ich denke, dies ist ein iOS 7-Fehler. Durch Erzwingen des Hauptthreads werden alle UIKit-bezogenen Nachrichten ausgeführt. Ich scheine darauf zu stoßen, wenn ich von einem anderen View-Controller zur Ansicht gehe. Ich aktualisiere die Daten in viewWillAppear. Ich konnte den Aufruf zum erneuten Laden der Daten- und Sammlungsansicht sehen, aber die Benutzeroberfläche wurde nicht aktualisiert. Erzwang den Haupt-Thread (UI-Thread) und es beginnt auf magische Weise zu arbeiten. Dies ist nur in IOS 7.
Shaunti Fondrisi
6
Dies ist nicht sehr sinnvoll, da Sie reloadData nicht aus dem Hauptthread heraus aufrufen können (Sie können Ansichten aus dem Hauptthread nicht aktualisieren). Dies ist möglicherweise ein Nebeneffekt, der aufgrund einiger Rennbedingungen zu dem führt, was Sie möchten.
Raphael Oliveira
7
Durch das Versenden der Hauptwarteschlange aus der Hauptwarteschlange wird die Ausführung nur bis zur nächsten Ausführungsschleife verzögert, sodass alles, was sich derzeit in der Warteschlange befindet, zuerst ausgeführt werden kann.
Joony
2
Vielen Dank!! Ich verstehe immer noch nicht, ob das Joony-Argument korrekt ist, weil die Kerndatenanforderung ihre Zeit in Anspruch nimmt und die Antwort verzögert ist oder weil ich Daten bei willDisplayCell neu lade.
Fidel López
1
Wow die ganze Zeit und das kommt immer noch auf. Dies ist in der Tat eine Rennbedingung oder hängt mit dem Lebenszyklus des Ansichtsereignisses zusammen. Ansicht "Will" erscheint wäre schon gezeichnet worden. Guter Einblick Joony, danke. Denken Sie, wir können diesen Artikel endlich auf "beantwortet" setzen?
Shaunti Fondrisi
64

In meinem Fall hat sich die Anzahl der Zellen / Abschnitte in der Datenquelle nie geändert, und ich wollte nur den sichtbaren Inhalt auf dem Bildschirm neu laden.

Ich habe es geschafft, dies zu umgehen, indem ich angerufen habe:

[self.collectionView reloadItemsAtIndexPaths:[self.collectionView indexPathsForVisibleItems]];

dann:

[self.collectionView reloadData];
Liamnichole
quelle
6
Diese Zeile führte zum Absturz meiner App - "*** Assertionsfehler in - [UICollectionView _endItemAnimations], /SourceCache/UIKit_Sim/UIKit-2935.137/UICollectionView.m:3840"
Lugubrious
@Lugubrious Sie führen wahrscheinlich gleichzeitig andere Animationen durch. Versuchen Sie, sie in einen performBatchUpdates:completion:Block zu setzen?
Liamnichols
Das hat bei mir funktioniert, aber ich bin mir nicht sicher, warum das notwendig ist. Irgendeine Idee, worum es geht?
Jon Evans
@ JonEvans Leider habe ich keine Ahnung. Ich glaube, es ist eine Art Fehler in iOS, nicht sicher, ob er in späteren Versionen behoben wurde oder nicht, da ich seitdem nicht mehr getestet habe und das Projekt, in dem ich das Problem hatte, nein ist länger mein Problem :)
Liamnichols
1
Dieser Bug ist einfach nur Blödsinn! Alle meine Zellen verschwanden zufällig, als ich meine collectionView neu lud, nur wenn ich einen bestimmten Zelltyp in meiner Sammlung hatte. Ich habe zwei Tage verloren, weil ich nicht verstehen konnte, was passiert ist, und jetzt, da ich Ihre Lösung angewendet habe und sie funktioniert, verstehe ich immer noch nicht, warum sie jetzt funktioniert. Das ist so frustrierend! Trotzdem danke für die Hilfe: D !!
CyberDandy
26

Ich hatte genau das gleiche Problem, aber ich konnte herausfinden, was falsch lief. In meinem Fall habe ich reloadData aus der collectionView aufgerufen: cellForItemAtIndexPath: was nicht korrekt zu sein scheint .

Das Versenden des Aufrufs von reloadData an die Hauptwarteschlange hat das Problem ein für alle Mal behoben.

  dispatch_async(dispatch_get_main_queue(), ^{
    [self.collectionView reloadData];
  });
Anton Matosov
quelle
1
Kannst du mir sagen, was diese Zeile für [self.collectionData.collectionViewLayout invalidateLayout] ist?
iOSDeveloper
Dies löste es auch für mich - in meinem Fall reloadDatawurde es von einem Änderungsbeobachter angerufen.
Sudo Make Install
Dies gilt auch fürcollectionView(_:willDisplayCell:forItemAtIndexPath:)
Stefan Arambasich
20

Das Nachladen einiger Elemente hat bei mir nicht funktioniert. In meinem Fall und nur weil die von mir verwendete collectionView nur einen Abschnitt enthält, lade ich diesen bestimmten Abschnitt einfach neu. Diesmal werden die Inhalte korrekt neu geladen. Seltsam, dass dies nur unter iOS 7 (7.0.3) geschieht.

[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
miguelsanchez
quelle
12

Ich hatte das gleiche Problem mit reloadData unter iOS 7. Nach einer langen Debug-Sitzung fand ich das Problem.

Unter iOS7 bricht reloadData in UICollectionView frühere Updates, die noch nicht abgeschlossen wurden, nicht ab (Updates, die innerhalb von performBatchUpdates: block aufgerufen wurden).

Die beste Lösung, um diesen Fehler zu beheben, besteht darin, alle aktuell verarbeiteten Updates zu stoppen und reloadData aufzurufen. Ich habe keine Möglichkeit gefunden, einen Block von performBatchUpdates abzubrechen oder zu stoppen. Um den Fehler zu beheben, habe ich daher ein Flag gespeichert, das angibt, ob ein performBatchUpdates-Block vorhanden ist, der gerade verarbeitet wird. Wenn es keinen Update-Block gibt, der gerade verarbeitet wird, kann ich reloadData sofort aufrufen und alles funktioniert wie erwartet. Wenn es einen Update-Block gibt, der gerade verarbeitet wird, rufe ich reloadData für den gesamten Block von performBatchUpdates auf.

user2459624
quelle
Wo führen Sie alle Aktualisierungen in performBatchUpdate durch? Einige in einigen aus? Alles raus? Sehr interessanter Beitrag.
VaporwareWolf
Ich verwende die Sammlungsansicht mit NSFetchedResultsController, um Daten aus CoreData anzuzeigen. Wenn der NSFetchedResultsController-Delegat über Änderungen benachrichtigt, sammle ich alle Aktualisierungen und rufe sie in performBatchUpdates auf. Wenn das Anforderungsprädikat NSFetchedResultsController geändert wird, muss reloadData aufgerufen werden.
user2459624
Dies ist eigentlich eine gute Antwort auf die Frage. Wenn Sie ein reloadItems () (das animiert ist) und dann reloadData () ausführen, werden Zellen übersprungen.
Bio
12

Swift 5 - 4 - 3

// GCD    
DispatchQueue.main.async(execute: collectionView.reloadData)

// Operation
OperationQueue.main.addOperation(collectionView.reloadData)

Swift 2

// Operation
NSOperationQueue.mainQueue().addOperationWithBlock(collectionView.reloadData)
Dimpiax
quelle
4

Ich hatte auch dieses Problem. Zufällig habe ich oben in der Sammlungsansicht eine Schaltfläche hinzugefügt, um das Neuladen zum Testen zu erzwingen - und plötzlich wurden die Methoden aufgerufen.

Fügen Sie auch einfach etwas hinzu, das so einfach ist wie

UIView *aView = [UIView new];
[collectionView addSubView:aView];

würde dazu führen, dass die Methoden aufgerufen werden

Außerdem habe ich mit der Rahmengröße herumgespielt - und voila, die Methoden wurden aufgerufen.

Es gibt viele Fehler mit iOS7 UICollectionView.

Avner Barr
quelle
Ich bin froh zu sehen (auf bestimmte Weise), dass auch andere dieses Problem haben. Vielen Dank für die Problemumgehung.
VaporwareWolf
3

Sie können diese Methode verwenden

[collectionView reloadItemsAtIndexPaths:arayOfAllIndexPaths];

Sie können alle indexPathObjekte Ihres UICollectionViewArrays in das Array arrayOfAllIndexPathseinfügen, indem Sie die Schleife für alle Abschnitte und Zeilen mithilfe der folgenden Methode iterieren

[aray addObject:[NSIndexPath indexPathForItem:j inSection:i]];

Ich hoffe du hast es verstanden und es kann dein Problem lösen. Wenn Sie weitere Erklärungen benötigen, antworten Sie bitte.

iDevAmit
quelle
3

Die Lösung von Shaunti Fondrisi ist nahezu perfekt. Aber solch ein Code oder Codes wie das Einreihen der Ausführung von UICollectionView's reloadData()in NSOperationQueue' s mainQueuesetzen den Ausführungszeitpunkt tatsächlich an den Anfang der nächsten Ereignisschleife in der Ausführungsschleife, wodurch dieUICollectionView Aktualisierung mit einem Flick bewirken könnte.

Um dieses Problem zu lösen. Wir müssen den Ausführungszeitpunkt desselben Codeteils an das Ende der aktuellen Ereignisschleife setzen, aber nicht an den Anfang der nächsten. Und das können wir erreichen, indem wir davon Gebrauch machen CFRunLoopObserver.

CFRunLoopObserver beobachtet alle Warteaktivitäten der Eingabequelle und die Ein- und Ausstiegsaktivität der Ausführungsschleife.

public struct CFRunLoopActivity : OptionSetType {
    public init(rawValue: CFOptionFlags)

    public static var Entry: CFRunLoopActivity { get }
    public static var BeforeTimers: CFRunLoopActivity { get }
    public static var BeforeSources: CFRunLoopActivity { get }
    public static var BeforeWaiting: CFRunLoopActivity { get }
    public static var AfterWaiting: CFRunLoopActivity { get }
    public static var Exit: CFRunLoopActivity { get }
    public static var AllActivities: CFRunLoopActivity { get }
}

Unter diesen Aktivitäten .AfterWaitingkann beobachtet werden, wann die aktuelle Ereignisschleife kurz vor dem Ende steht, und.BeforeWaiting kann beobachtet werden, wenn die nächste Ereignisschleife gerade begonnen hat.

Da es nur eine NSRunLoopInstanz pro gibt NSThreadund NSRunLoopgenau die antreibt NSThread, können wir berücksichtigen, dass Zugriffe von derselben NSRunLoopInstanz immer niemals Threads kreuzen.

Basierend auf den zuvor genannten Punkten können wir jetzt den Code schreiben: einen NSRunLoop-basierten Task-Dispatcher:

import Foundation
import ObjectiveC

public struct Weak<T: AnyObject>: Hashable {
    private weak var _value: T?
    public weak var value: T? { return _value }
    public init(_ aValue: T) { _value = aValue }

    public var hashValue: Int {
        guard let value = self.value else { return 0 }
        return ObjectIdentifier(value).hashValue
    }
}

public func ==<T: AnyObject where T: Equatable>(lhs: Weak<T>, rhs: Weak<T>)
    -> Bool
{
    return lhs.value == rhs.value
}

public func ==<T: AnyObject>(lhs: Weak<T>, rhs: Weak<T>) -> Bool {
    return lhs.value === rhs.value
}

public func ===<T: AnyObject>(lhs: Weak<T>, rhs: Weak<T>) -> Bool {
    return lhs.value === rhs.value
}

private var dispatchObserverKey =
"com.WeZZard.Nest.NSRunLoop.TaskDispatcher.DispatchObserver"

private var taskQueueKey =
"com.WeZZard.Nest.NSRunLoop.TaskDispatcher.TaskQueue"

private var taskAmendQueueKey =
"com.WeZZard.Nest.NSRunLoop.TaskDispatcher.TaskAmendQueue"

private typealias DeallocFunctionPointer =
    @convention(c) (Unmanaged<NSRunLoop>, Selector) -> Void

private var original_dealloc_imp: IMP?

private let swizzled_dealloc_imp: DeallocFunctionPointer = {
    (aSelf: Unmanaged<NSRunLoop>,
    aSelector: Selector)
    -> Void in

    let unretainedSelf = aSelf.takeUnretainedValue()

    if unretainedSelf.isDispatchObserverLoaded {
        let observer = unretainedSelf.dispatchObserver
        CFRunLoopObserverInvalidate(observer)
    }

    if let original_dealloc_imp = original_dealloc_imp {
        let originalDealloc = unsafeBitCast(original_dealloc_imp,
            DeallocFunctionPointer.self)
        originalDealloc(aSelf, aSelector)
    } else {
        fatalError("The original implementation of dealloc for NSRunLoop cannot be found!")
    }
}

public enum NSRunLoopTaskInvokeTiming: Int {
    case NextLoopBegan
    case CurrentLoopEnded
    case Idle
}

extension NSRunLoop {

    public func perform(closure: ()->Void) -> Task {
        objc_sync_enter(self)
        loadDispatchObserverIfNeeded()
        let task = Task(self, closure)
        taskQueue.append(task)
        objc_sync_exit(self)
        return task
    }

    public override class func initialize() {
        super.initialize()

        struct Static {
            static var token: dispatch_once_t = 0
        }
        // make sure this isn't a subclass
        if self !== NSRunLoop.self {
            return
        }

        dispatch_once(&Static.token) {
            let selectorDealloc: Selector = "dealloc"
            original_dealloc_imp =
                class_getMethodImplementation(self, selectorDealloc)

            let swizzled_dealloc = unsafeBitCast(swizzled_dealloc_imp, IMP.self)

            class_replaceMethod(self, selectorDealloc, swizzled_dealloc, "@:")
        }
    }

    public final class Task {
        private let weakRunLoop: Weak<NSRunLoop>

        private var _invokeTiming: NSRunLoopTaskInvokeTiming
        private var invokeTiming: NSRunLoopTaskInvokeTiming {
            var theInvokeTiming: NSRunLoopTaskInvokeTiming = .NextLoopBegan
            guard let amendQueue = weakRunLoop.value?.taskAmendQueue else {
                fatalError("Accessing a dealloced run loop")
            }
            dispatch_sync(amendQueue) { () -> Void in
                theInvokeTiming = self._invokeTiming
            }
            return theInvokeTiming
        }

        private var _modes: NSRunLoopMode
        private var modes: NSRunLoopMode {
            var theModes: NSRunLoopMode = []
            guard let amendQueue = weakRunLoop.value?.taskAmendQueue else {
                fatalError("Accessing a dealloced run loop")
            }
            dispatch_sync(amendQueue) { () -> Void in
                theModes = self._modes
            }
            return theModes
        }

        private let closure: () -> Void

        private init(_ runLoop: NSRunLoop, _ aClosure: () -> Void) {
            weakRunLoop = Weak<NSRunLoop>(runLoop)
            _invokeTiming = .NextLoopBegan
            _modes = .defaultMode
            closure = aClosure
        }

        public func forModes(modes: NSRunLoopMode) -> Task {
            if let amendQueue = weakRunLoop.value?.taskAmendQueue {
                dispatch_async(amendQueue) { [weak self] () -> Void in
                    self?._modes = modes
                }
            }
            return self
        }

        public func when(invokeTiming: NSRunLoopTaskInvokeTiming) -> Task {
            if let amendQueue = weakRunLoop.value?.taskAmendQueue {
                dispatch_async(amendQueue) { [weak self] () -> Void in
                    self?._invokeTiming = invokeTiming
                }
            }
            return self
        }
    }

    private var isDispatchObserverLoaded: Bool {
        return objc_getAssociatedObject(self, &dispatchObserverKey) !== nil
    }

    private func loadDispatchObserverIfNeeded() {
        if !isDispatchObserverLoaded {
            let invokeTimings: [NSRunLoopTaskInvokeTiming] =
            [.CurrentLoopEnded, .NextLoopBegan, .Idle]

            let activities =
            CFRunLoopActivity(invokeTimings.map{ CFRunLoopActivity($0) })

            let observer = CFRunLoopObserverCreateWithHandler(
                kCFAllocatorDefault,
                activities.rawValue,
                true, 0,
                handleRunLoopActivityWithObserver)

            CFRunLoopAddObserver(getCFRunLoop(),
                observer,
                kCFRunLoopCommonModes)

            let wrappedObserver = NSAssociated<CFRunLoopObserver>(observer)

            objc_setAssociatedObject(self,
                &dispatchObserverKey,
                wrappedObserver,
                .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    private var dispatchObserver: CFRunLoopObserver {
        loadDispatchObserverIfNeeded()
        return (objc_getAssociatedObject(self, &dispatchObserverKey)
            as! NSAssociated<CFRunLoopObserver>)
            .value
    }

    private var taskQueue: [Task] {
        get {
            if let taskQueue = objc_getAssociatedObject(self,
                &taskQueueKey)
                as? [Task]
            {
                return taskQueue
            } else {
                let initialValue = [Task]()

                objc_setAssociatedObject(self,
                    &taskQueueKey,
                    initialValue,
                    .OBJC_ASSOCIATION_RETAIN_NONATOMIC)

                return initialValue
            }
        }
        set {
            objc_setAssociatedObject(self,
                &taskQueueKey,
                newValue,
                .OBJC_ASSOCIATION_RETAIN_NONATOMIC)

        }
    }

    private var taskAmendQueue: dispatch_queue_t {
        if let taskQueue = objc_getAssociatedObject(self,
            &taskAmendQueueKey)
            as? dispatch_queue_t
        {
            return taskQueue
        } else {
            let initialValue =
            dispatch_queue_create(
                "com.WeZZard.Nest.NSRunLoop.TaskDispatcher.TaskAmendQueue",
                DISPATCH_QUEUE_SERIAL)

            objc_setAssociatedObject(self,
                &taskAmendQueueKey,
                initialValue,
                .OBJC_ASSOCIATION_RETAIN_NONATOMIC)

            return initialValue
        }
    }

    private func handleRunLoopActivityWithObserver(observer: CFRunLoopObserver!,
        activity: CFRunLoopActivity)
        -> Void
    {
        var removedIndices = [Int]()

        let runLoopMode: NSRunLoopMode = currentRunLoopMode

        for (index, eachTask) in taskQueue.enumerate() {
            let expectedRunLoopModes = eachTask.modes
            let expectedRunLoopActivitiy =
            CFRunLoopActivity(eachTask.invokeTiming)

            let runLoopModesMatches = expectedRunLoopModes.contains(runLoopMode)
                || expectedRunLoopModes.contains(.commonModes)

            let runLoopActivityMatches =
            activity.contains(expectedRunLoopActivitiy)

            if runLoopModesMatches && runLoopActivityMatches {
                eachTask.closure()
                removedIndices.append(index)
            }
        }

        taskQueue.removeIndicesInPlace(removedIndices)
    }
}

extension CFRunLoopActivity {
    private init(_ invokeTiming: NSRunLoopTaskInvokeTiming) {
        switch invokeTiming {
        case .NextLoopBegan:        self = .AfterWaiting
        case .CurrentLoopEnded:     self = .BeforeWaiting
        case .Idle:                 self = .Exit
        }
    }
}

Mit dem vorherigen Code können wir nun die Ausführung von UICollectionView's reloadData()durch einen solchen Code an das Ende der aktuellen Ereignisschleife senden:

NSRunLoop.currentRunLoop().perform({ () -> Void in
     collectionView.reloadData()
    }).when(.CurrentLoopEnded)

Tatsächlich befand sich ein solcher NSRunLoop-basierter Task-Dispatcher bereits in einem meiner persönlich verwendeten Frameworks: Nest. Und hier ist das Repository auf GitHub: https://github.com/WeZZard/Nest

WeZZard
quelle
2
 dispatch_async(dispatch_get_main_queue(), ^{

            [collectionView reloadData];
            [collectionView layoutIfNeeded];
            [collectionView reloadData];


        });

es hat bei mir funktioniert.

Prajakta
quelle
1

Vielen Dank zunächst für diesen Thread, sehr hilfreich. Ich hatte ein ähnliches Problem mit Reload Data, außer dass das Symptom war, dass bestimmte Zellen nicht mehr dauerhaft ausgewählt werden konnten, während andere dies konnten. Kein Aufruf der indexPathsForSelectedItems-Methode oder einer gleichwertigen Methode. Beim Debuggen wurde darauf hingewiesen, Daten neu zu laden. Ich habe beide Optionen oben ausprobiert. Am Ende wurde die Option ReloadItemsAtIndexPaths übernommen, da die anderen Optionen in meinem Fall nicht funktionierten oder die Sammlungsansicht etwa eine Millisekunde lang blinken ließen. Der folgende Code funktioniert gut:

NSMutableArray *indexPaths = [[NSMutableArray alloc] init]; 
NSIndexPath *indexPath;
for (int i = 0; i < [self.assets count]; i++) {
         indexPath = [NSIndexPath indexPathForItem:i inSection:0];
         [indexPaths addObject:indexPath];
}
[collectionView reloadItemsAtIndexPaths:indexPaths];`
stephane
quelle
0

Es passierte auch bei mir in iOS 8.1 sdk, aber ich habe es richtig verstanden, als ich bemerkte, dass datasourcedie Methode auch nach dem Aktualisieren der Methode numberOfItemsInSection:nicht die neue Anzahl von Elementen zurückgab. Ich habe die Zählung aktualisiert und zum Laufen gebracht.

Vinay Jain
quelle
Wie haben Sie diese Anzahl bitte aktualisiert? Alle oben genannten Methoden haben in Swift 3 bei mir nicht funktioniert.
? funktioniert
0

Legen Sie UICollectionView.contentInset fest? entferne den linken und rechten RandInset, alles ist in Ordnung, nachdem ich sie entfernt habe, der Fehler existiert immer noch in iOS8.3.

Jiang Qi
quelle
0

Überprüfen Sie, ob jede der UICollectionView Delegate-Methoden das tut, was Sie von ihr erwarten. Zum Beispiel, wenn

collectionView:layout:sizeForItemAtIndexPath:

gibt keine gültige Größe zurück, das Nachladen funktioniert nicht ...

Oded Regev
quelle
0

Versuchen Sie diesen Code.

 NSArray * visibleIdx = [self.collectionView indexPathsForVisibleItems];

    if (visibleIdx.count) {
        [self.collectionView reloadItemsAtIndexPaths:visibleIdx];
    }
Liki qu
quelle
0

So hat es bei mir in Swift 4 funktioniert

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

let cell = campaignsCollection.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell

cell.updateCell()

    // TO UPDATE CELLVIEWS ACCORDINGLY WHEN DATA CHANGES
    DispatchQueue.main.async {
        self.campaignsCollection.reloadData()
    }

    return cell
}
Wissa
quelle
-1
inservif (isInsertHead) {
   [self insertItemsAtIndexPaths:tmpPoolIndex];
   NSArray * visibleIdx = [self indexPathsForVisibleItems];
   if (visibleIdx.count) {
       [self reloadItemsAtIndexPaths:visibleIdx];
   }
}else if (isFirstSyncData) {
    [self reloadData];
}else{
   [self insertItemsAtIndexPaths:tmpPoolIndex];
}
zszen
quelle