ios uicollectionview ios7

UICollectionView reloadData no funciona correctamente en iOS 7



ios7 (17)

Swift 4 - 3

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

Swift 2

// Operation NSOperationQueue.mainQueue().addOperationWithBlock(collectionView.reloadData)

He estado actualizando mis aplicaciones para ejecutarlas en iOS 7, lo que está funcionando sin problemas en su mayor parte. Me he dado cuenta en más de una aplicación que el método reloadData de un UICollectionViewController no está actuando como solía hacerlo.

UICollectionViewController , completaré el UICollectionView con algunos datos de forma normal. Esto funciona genial en la primera vez. Sin embargo, si solicito nuevos datos ( UICollectionViewDataSource el UICollectionViewDataSource ) y luego reloadData , consultará el origen de datos para numberOfItemsInSection y numberOfSectionsInCollectionView , pero no parece llamar a cellForItemAtIndexPath el número de veces adecuado.

Si cambio el código solo para volver a cargar una sección, funcionará correctamente. Este no es un problema para mí para cambiar esto, pero no creo que deba hacerlo. reloadData debe volver a cargar todas las celdas visibles de acuerdo con la documentación.

¿Alguien más ha visto esto?


¿Estableces UICollectionView.contentInset? elimine los bordes izquierdo y derecho, todo está bien después de que los elimine, el error aún existe en iOS8.3.


Así es como me funcionó 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 }


Compruebe que cada uno de los métodos de delegado UICollectionView haga lo que espera que haga. Por ejemplo, si

collectionView:layout:sizeForItemAtIndexPath:

no devuelve un tamaño válido, la recarga no funcionará ...


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

Logré solucionar esto llamando:

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

entonces:

[self.collectionView reloadData];


Forzar esto en el hilo principal:

dispatch_async(dispatch_get_main_queue(), ^ { [self.collectionView reloadData]; });


Gracias en primer lugar por este hilo, muy útil. Tuve un problema similar con Reload Data, excepto que el síntoma era que las células específicas ya no podían seleccionarse de forma permanente, mientras que otras sí podían. Sin llamada al método indexPathsForSelectedItems o equivalente. Depuración apuntada a Reload Data. Intenté ambas opciones arriba; y terminé adoptando la opción ReloadItemsAtIndexPaths ya que las otras opciones no funcionaron en mi caso o hacían que la vista de la colección parpadeara durante un milisegundo más o menos. El siguiente código funciona bien:

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];`


La solución dada por Shaunti Fondrisi es casi perfecta. Pero una pieza de código o códigos como enqueue la ejecución de UICollectionView reloadData() de NSOperationQueue a NSOperationQueue de mainQueue hecho pone el tiempo de ejecución al comienzo del próximo ciclo de eventos en el ciclo de ejecución, lo que podría hacer que la actualización de UICollectionView con un película.

Para resolver este problema Debemos poner el tiempo de ejecución de la misma pieza de código al final del ciclo de evento actual pero no al principio de la siguiente. Y podemos lograr esto haciendo uso de CFRunLoopObserver .

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

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, se puede observar. .AfterWaiting cuando el ciclo de evento actual está a punto de terminar, y .BeforeWaiting se puede observar cuando el siguiente ciclo de evento acaba de comenzar.

Como solo hay una instancia de NSRunLoop por cada NSThread y NSRunLoop controla exactamente el NSThread , podemos considerar que los accesos provenientes de la misma instancia de NSRunLoop nunca cruzan los hilos.

En función de los puntos mencionados anteriormente, ahora podemos escribir el código: un despachador de tareas basado en NSRunLoop:

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 anterior, ahora podemos enviar la ejecución de UICollectionView reloadData() al final del bucle de evento actual mediante un fragmento de código:

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

De hecho, un despachador de tareas basado en NSRunLoop ya ha estado en uno de mis marcos de trabajo personales: Nest. Y aquí está su repositorio en GitHub: https://github.com/WeZZard/Nest


Me pasó a mí también en iOS 8.1 sdk, pero lo entendí correctamente cuando noté que incluso después de actualizar el datasource el método numberOfItemsInSection: no devolvía el nuevo recuento de elementos. Actualicé el conteo y lo puse en funcionamiento.


Puedes usar este método

[collectionView reloadItemsAtIndexPaths:arayOfAllIndexPaths];

Puede agregar todos los objetos indexPath de su UICollectionView en array arrayOfAllIndexPaths iterando el ciclo para todas las secciones y filas con el uso del siguiente método

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

Espero que lo haya entendido y que pueda resolver su problema. Si necesita más explicaciones, responda.


Recargar algunos artículos no funcionó para mí. En mi caso, y solo porque el collectionView que estoy usando tiene solo una sección, simplemente recargo esa sección en particular. Esta vez, los contenidos se vuelven a cargar correctamente. Es extraño que esto solo ocurra en iOS 7 (7.0.3)

[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];


Tenía exactamente el mismo problema, sin embargo, logré encontrar lo que estaba pasando mal. En mi caso, estaba llamando a reloadData desde collectionView: cellForItemAtIndexPath: que parece no ser correcto.

El envío de call de reloadData a la cola principal solucionó el problema de una vez y para siempre.

dispatch_async(dispatch_get_main_queue(), ^{ [self.collectionView reloadData]; });


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

En iOS7, reloadData en UICollectionView no cancela las actualizaciones anteriores que aún no se han completado (las actualizaciones que llaman dentro de performBatchUpdates: bloque).

La mejor solución para resolver este error es detener todas las actualizaciones que actualmente se procesan y llamar a reloadData. No encontré la forma de cancelar o detener un bloque de performBatchUpdates. Por lo tanto, para resolver el error, guardé una bandera que indica si hay un bloque performBatchUpdates que se procesa actualmente. Si no hay un bloque de actualización procesado actualmente, puedo llamar a reloadData inmediatamente y todo funciona como se espera. Si hay un bloque de actualización que se procesa actualmente, llamaré a reloadData en el bloque completo de performBatchUpdates.


Yo también tuve este problema. Por casualidad, agregué un botón en la vista de colección para forzar la recarga para probar y, de repente, los métodos comenzaron a llamarse.

También solo agregar algo tan simple como

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

causaría que los métodos sean llamados

También jugué con el tamaño de fotograma y voila los métodos fueron llamados.

Hay muchos errores con iOS7 UICollectionView.


prueba este código

NSArray * visibleIdx = [self.collectionView indexPathsForVisibleItems]; if (visibleIdx.count) { [self.collectionView reloadItemsAtIndexPaths:visibleIdx]; }


dispatch_async(dispatch_get_main_queue(), ^{ [collectionView reloadData]; [collectionView layoutIfNeeded]; [collectionView reloadData]; });

funcionó para mí.


inservif (isInsertHead) { [self insertItemsAtIndexPaths:tmpPoolIndex]; NSArray * visibleIdx = [self indexPathsForVisibleItems]; if (visibleIdx.count) { [self reloadItemsAtIndexPaths:visibleIdx]; } }else if (isFirstSyncData) { [self reloadData]; }else{ [self insertItemsAtIndexPaths:tmpPoolIndex]; }