He estado actualizando mis aplicaciones que se ejecutan en iOS 7 que va bien para la mayor parte. He notado que en más de una aplicación que el reloadData método de un UICollectionViewController no actúa muy bien cómo funcionaba.

Voy a cargar el UICollectionViewController, rellenar el UICollectionView con algunos datos como normal. Esto funciona muy bien en el primer tiempo. Sin embargo si puedo solicitar nuevos datos (llenar el UICollectionViewDataSource) y, a continuación, llamar a reloadData, va a la consulta de la fuente de datos para numberOfItemsInSection y numberOfSectionsInCollectionView, pero no parece llamar cellForItemAtIndexPath el número apropiado de veces.

Si puedo cambiar el código para que sólo cargar una sección, entonces funcionará correctamente. Esto no es ningún problema para mí a cambio de estos, pero creo que no debería tener. reloadData debe volver a cargar todas las celdas visibles de acuerdo a la documentación.

Ha nadie ve esto?

  • Mismo aquí, en su iOS7GM, bien trabajado antes. Me di cuenta de que llamar a reloadData después de la viewDidAppear parece resolver el problema, su horrible solución, y de las necesidades de revisión. Espero que alguien ayude a salir de aquí.
  • Teniendo el mismo problema. Código utilizado para trabajar bien en iOS6 . ahora no llamar cellforitematindexpath aunque volviendo número adecuado de células
  • Fue esta fijo en un post-7.0?
  • Estoy todavía se enfrentan a cuestiones relativas a esta cuestión.
  • Problema Similar después de cambiar [collectionView setFrame] sobre la marcha, siempre dequeues de una célula y que es, independientemente del número en el origen de datos. Probado de todo, aquí y más, y no puede conseguir alrededor de él.
  • Creo que es fijo en 7.1
  • Tengo el mismo problema en IOS 9.2, @jasonIM ‘s solución resuelve mi problema. pero en mi caso, viewWillAppear es útil suficiente.
  • Yo también tenía problemas con reloadData en realidad no actualización de toda la muestra de células, incluso a pesar de que la numberOfItemsInSection ha cambiado. Esto es con iOS 9.3. La solución fue de envío asíncrono el volver a cargar los datos en el hilo principal para que se ejecuta en la próxima ejecución del bucle.

17 Comentarios

  1. 71

    La fuerza esta en el hilo principal:

    dispatch_async(dispatch_get_main_queue(), ^ {
        [self.collectionView reloadData];
    });
    • Usted es bienvenido a explicar más..
    • No estoy seguro de si puedo explicar más. Después de buscar, investigando, probando y probando. Yo siento que esto es un bug de iOS 7. Forzar el hilo principal de ejecución de todos los UIKit los mensajes relacionados. Me parece que en esto cuando apareciendo a la vista desde otro punto de vista controlador. Actualizar los datos en viewWillAppear. Pude ver los datos y vista de colección de recarga de la llamada, pero la interfaz de usuario no se actualizó. Forzado el hilo principal (subproceso de interfaz de usuario), y por arte de magia empieza a trabajar. Esta es sólo en IOS 7.
    • No tiene mucho sentido porque no se puede llamar reloadData fuera del hilo principal (no se puede actualizar vistas de que el hilo principal), así que esto tal vez es un efecto secundario que resulta en lo que usted quiere, debido a algunas condiciones de carrera.
    • Despacho en la cola principal de la cola principal sólo retrasos en la ejecución hasta la siguiente ejecución del bucle, permitiendo que todo lo que se encuentra en la cola de una oportunidad para ejecutar en primer lugar.
    • Gracias!! Todavía no entiendo si Joony argumento es correcto debido a que los datos de los núcleos de solicitud de consumir es el momento y es la respuesta se retrasa o porque me voy a volver a cargar los datos en willDisplayCell.
    • no funciona para mí 🙁
    • Wow todo este tiempo y todavía viene. Esto es de hecho una condición de carrera, o relacionados con la vista de eventos de ciclo de vida. vista de la «Voluntad» de aparecer hubiera sido dibujado ya. Muy buena idea Joony, Gracias. Creo que se puede ajustar este elemento para «respondió» finalmente?

  2. 63

    En mi caso, el número de células/secciones en el origen de datos nunca ha cambiado, y yo solo quería volver a cargar el contenido visible en la pantalla..

    Me las arreglé para conseguir alrededor de esta llamando:

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

    a continuación:

    [self.collectionView reloadData];
    • La línea que hizo que mi aplicación se bloquee – «*** error de Aserción en -[UICollectionView _endItemAnimations], /SourceCache/UIKit_Sim/UIKit-2935.137/UICollectionView.m:3840»
    • Usted probablemente está realizando otras animaciones al mismo tiempo.. trate de ponerlos en un performBatchUpdates:completion: bloque?
    • Esto funcionó para mí, pero no estoy seguro de entender por qué es necesaria. Alguna idea de cuál es el problema?
    • Por desgracia no tengo idea.. yo creo que es algún tipo de bug en iOS, no se si se ha resuelto en versiones posteriores o no, aunque como yo no lo he probado ya y el proyecto tenía el problema en el ya no es mi problema 🙂
    • Bueno, me alegra saber que no es un problema para usted por lo menos! Voy a agregar a esto que yo tenía algo de estrellarse problemas con este código hasta que he invertido el orden de las llamadas (por lo reloadData primero y luego reloadItemsAtIndexPaths), pero como yo no sé por qué funciona en todos ciertamente no puedo explicar por qué los que ayudaron!
    • A mí me funciona. Gracias!
    • Este error es pura mierda ! Todas mis células fueron – al azar – desapareciendo cuando me vuelve a cargar mi collectionView, sólo si tuviera un tipo específico de célula en mi colección. He perdido dos días, porque no podía entender lo que estaba sucediendo, y ahora que he aplicado la solución y de que funciona, yo todavía no entiendo por qué está trabajando ahora. Eso es tan frustrante ! De todos modos, gracias por la ayuda 😀 !!
    • No sé por qué pero tenía que seguir esta respuesta para obtener celdas visibles para alinear & tamaño correctamente en la rotación de cambio. Gracias!
    • la respuesta debe trabajar para que más y el que menos rendimiento pesado
    • No sé por qué pero esto me salvó el día. Gracias 🙂
    • Muchas gracias. Se soluciono mi problema.
    • En mi caso, reloadItemsAtIndexPaths tuvo que ser llamado después reloadData debido a un cambio en los datos.

  3. 26

    Yo tenía exactamente el mismo problema, sin embargo, me las arreglé para encontrar lo que estaba pasando mal.
    En mi caso me estaba llamando reloadData de la collectionView:cellForItemAtIndexPath: que parece no ser la correcta.

    Envío de llamada de reloadData a la cola principal solucionado el problema de una vez y para siempre.

      dispatch_async(dispatch_get_main_queue(), ^{
        [self.collectionView reloadData];
      });
    • u puede decirme lo que esta línea es para [auto.collectionData.collectionViewLayout invalidateLayout];
    • No debería estar allí. Gracias por decírmelo.
    • Ya esta resuelto para mí también-en mi caso reloadData estaba siendo llamado por un cambio de observador.
    • buen trabajo. Esta solucionado mi problema. Muchas gracias
    • También esto se aplica a collectionView(_:willDisplayCell:forItemAtIndexPath:)
  4. 20

    La recarga de algunos elementos no funcionó para mí. En mi caso, y sólo porque el collectionView estoy usando solo dispone de una sección, yo simplemente recargar esa sección en particular.
    Esta vez el contenido correctamente reloaded.
    Raro que esto es sólo sucede en iOS 7 (7.0.3)

    [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
  5. 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)
  6. 11

    Tuve el mismo problema con reloadData en iOS 7. Después de una larga sesión de depuración, he encontrado el problema.

    En iOS7, reloadData en UICollectionView no cancelar las actualizaciones anteriores que no han terminado todavía (Actualizaciones que se llama dentro de performBatchUpdates: bloque).

    La mejor solución para resolver este error, es el de detener todas las actualizaciones que actualmente procesado y llame a reloadData. No he encontrado una forma de cancelar o detener un bloque de performBatchUpdates. Por lo tanto, para resolver el error, he guardado una bandera que indica si hay un performBatchUpdates bloque que actualmente procesado. Si no hay una actualización de bloque que actualmente procesado, puedo llamar a reloadData de inmediato y que todo funcione como se espera. Si hay una actualización de bloque que actualmente se procesa, voy a llamar a reloadData en el bloque completo de performBatchUpdates.

    • Donde realizar todos los de su actualización dentro de performBatchUpdate? De alguna salida? Todo? Muy interesante el post.
    • Estoy usando la vista de colección con NSFetchedResultsController para mostrar los datos de CoreData. Cuando el NSFetchedResultsController delegado notificar sobre cambios, que reúnen a todas las actualizaciones y llamar dentro de performBatchUpdates. Cuando el NSFetchedResultsController solicitud predicado es cambiado, reloadData debe ser llamado.
  7. 4

    También he tenido este problema. Por casualidad he añadido un botón en la parte superior de la collectionview con el fin de forzar la recarga de las pruebas – y, de repente, los métodos comenzó a recibir llamados.

    También acaba de agregar algo tan simple como

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

    haría que los métodos para

    También he jugado con el tamaño de la trama – y voila los métodos que se estaban recibiendo la llamada.

    Hay un montón de errores con iOS7 UICollectionView.

    • Me alegro de ver (en ciertas maneras que otros están experimentando este problema así. Gracias por la solución.
  8. 3

    Puede utilizar este método

    [collectionView reloadItemsAtIndexPaths:arayOfAllIndexPaths];

    Usted puede agregar todos los indexPath objetos de su UICollectionView en la matriz de arrayOfAllIndexPaths por iteración del bucle para todas las secciones y filas con el uso de método a continuación

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

    Espero que te entiende y que puede resolver su problema. Si usted necesita más explicación, por favor responder.

  9. 3

    La solución dada por Shaunti Fondrisi es casi perfecta. Pero un trozo de código o códigos como poner en cola la ejecución de UICollectionView‘s reloadData() a NSOperationQueue‘s mainQueue, de hecho, pone el tiempo de ejecución para el comienzo del siguiente bucle de eventos en la ejecución del bucle, lo que podría hacer que el UICollectionView actualización con un gesto.

    Para resolver este problema. Debemos poner el tiempo de ejecución de la misma pieza de código al final de la corriente de bucle de eventos, pero no el comienzo de la siguiente. Y podemos lograr esto haciendo uso de CFRunLoopObserver.

    CFRunLoopObserver observa toda la fuente de entrada a la espera de actividades y la ejecución del bucle de entrada y salida de la actividad.

    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 }
    }

    Entre esas actividades, .AfterWaiting puede ser observado cuando la corriente de bucle de eventos está a punto de terminar, y .BeforeWaiting puede observarse en el siguiente bucle de eventos sólo ha comenzado.

    Como sólo hay una NSRunLoop instancia por NSThread y NSRunLoop exactamente unidades de la NSThread, podemos considerar que los accesos vienen de la misma NSRunLoop instancia siempre nunca se cruzan los hilos.

    Base a los puntos mencionados antes, ahora podemos escribir el código: un NSRunLoop tareas basadas en el 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
    }
    }
    }

    Con el código de antes, ahora podemos despachar la ejecución de UICollectionView‘s reloadData() a la final de la corriente de bucle de eventos, por ejemplo una pieza de código:

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

    De hecho, tal NSRunLoop tareas basadas en el despachador ha sido ya en uno de mis personales utilizados marco: Nido. Y aquí está su repositorio en GitHub: https://github.com/WeZZard/Nest

  10. 1

    Gracias ante todo por este hilo, muy útil. Yo tuve un problema similar con la Recarga de Datos excepto el síntoma era que las células específicas que ya no podía ser seleccionados en forma permanente, mientras que otros podrían. No llame a indexPathsForSelectedItems método o equivalente. La depuración señaló a Cargar los Datos. He probado las dos opciones de arriba ; y terminó adoptando el ReloadItemsAtIndexPaths opción como otras opciones que no funcionó en mi caso o estaban haciendo la vista de colección flash para un mili-segundo o así. El siguiente código de buenas obras:

    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];`
  11. 0

    Sucedió conmigo en iOS 8.1 sdk, pero tengo que corregir cuando me di cuenta de que incluso después de actualizar el datasource el método numberOfItemsInSection: no devuelve el nuevo recuento de los elementos. He actualizado la cuenta y tengo trabajo.

    • ¿cómo actualizar los que cuentan, por favor.. Todos los métodos anteriores han fallado a trabajar para mí en swift 3.
  12. 0

    ¿Que UICollectionView.contentInset? quitar la izquierda y la derecha edgeInset, todo está bien después de que me retire, el error sigue existiendo en iOS8.3 .

  13. 0

    Comprobar que cada uno de los UICollectionView los métodos de Delegado hace lo que usted espera que haga. Por ejemplo, si

    collectionView:layout:sizeForItemAtIndexPath:

    no devuelve un tamaño válido, la recarga no funciona…

  14. 0

    prueba este código.

     NSArray * visibleIdx = [self.collectionView indexPathsForVisibleItems];
    if (visibleIdx.count) {
    [self.collectionView reloadItemsAtIndexPaths:visibleIdx];
    }
  15. 0
     dispatch_async(dispatch_get_main_queue(), ^{
    [collectionView reloadData];
    [collectionView layoutIfNeeded];
    [collectionView reloadData];
    });

    que trabajó para mí.

  16. 0

    Aquí es cómo se trabajó para mí en Swift 4

    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
    }
  17. -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];
    }

Dejar respuesta

Please enter your comment!
Please enter your name here