Je me débats avec cela pendant des jours. Je vais apprécier toute aide.
J'ai un emplacement J'ai 2 écrans, dans la première, l'utilisateur ajoute des emplacements sur le contexte de la vue et ils sont ajoutés et récupérés sans problèmes. P> Maintenant, dans le deuxième écran, je souhaite récupérer des images en fonction de l'emplacement sélectionné dans le premier écran, puis affichez les images dans une vue de collection. Les images sont d'abord récupérées de Flickr, puis enregistrées dans la DB. P> Je veux enregistrer et récupérer des images sur un contexte d'arrière-plan et cela me provoque beaucoup de problèmes. p> Ceci est mon code d'économie: p> Comme vous pouvez le constater dans le code ci-dessus, je bâtiment nsmanagedObject sur le contexte d'arrière-plan en fonction de l'ID récupéré en fonction de l'ID récupéré de ceux sur le contexte de la vue. Chaque fois que Voici quelques extraits de code liés à la configuration de la FetchedResultSluStroller et à la mise à jour de la vue de la collection en fonction des changements dans le contexte ou dans le fetchedResultSlugroller. P> nsmanagedObject code> et une image
nsmanagedObject code>, ils ont une relation à une autre, c'est-à-dire un emplacement possède de nombreuses images. P>
sauveimagestodb code> est appelé i obtenir l'avertissement, alors quel est le problème? p>
application de terminaison en raison d'une exception non capturée 'NsinternalinconsiscyException', raison: "Mise à jour non valide: Nombre non valide d'éléments à la section 0. Le nombre d'éléments contenus dans une section existante après la mise à jour (4) doit être égal au nombre d'éléments contenus dans cette section avant la mise à jour (1), plus ou moins le nombre d'éléments insérés ou supprimés de cette section (1 inséré, 0 supprimé) et plus ou moins le nombre d'éléments déplacés dans ou en dehors. de cette section (0 déplacé, 0 déplacé). code> ' p>
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
guard let imagesCount = fetchedResultsController.fetchedObjects?.count else {return 0}
return imagesCount
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print ("cell data")
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCell", for: indexPath) as! ImageCell
//cell.placeImage.image = UIImage (named: "placeholder")
let imageObject = fetchedResultsController.object(at: indexPath)
let imageData = imageObject.image
let uiImage = UIImage (data: imageData!)
cell.placeImage.image = uiImage
return cell
}
func setUpFetchedResultsController () {
print ("setting up controller")
//Build a request for the Image ManagedObject
let fetchRequest : NSFetchRequest <Image> = Image.fetchRequest()
//Fetch the images only related to the images location
let locationObjectId = self.imagesLocation.objectID
let locationOnBackgroundContext = self.dataController.backgroundContext.object(with: locationObjectId) as! Location
let predicate = NSPredicate (format: "location == %@", locationOnBackgroundContext)
fetchRequest.predicate = predicate
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "location", ascending: true)]
fetchedResultsController = NSFetchedResultsController (fetchRequest: fetchRequest, managedObjectContext: dataController.backgroundContext, sectionNameKeyPath: nil, cacheName: "\(latLongString) images")
fetchedResultsController.delegate = self
do {
try fetchedResultsController.performFetch ()
} catch {
fatalError("couldn't retrive images for the selected location")
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
print ("object info changed in fecthed controller")
switch type {
case .insert:
print ("insert")
DispatchQueue.main.async {
print ("calling section items")
self.collectionView!.numberOfItems(inSection: 0)
self.collectionView.insertItems(at: [newIndexPath!])
}
break
case .delete:
print ("delete")
DispatchQueue.main.async {
self.collectionView!.numberOfItems(inSection: 0)
self.collectionView.deleteItems(at: [indexPath!])
}
break
case .update:
print ("update")
DispatchQueue.main.async {
self.collectionView!.numberOfItems(inSection: 0)
self.collectionView.reloadItems(at: [indexPath!])
}
break
case .move:
print ("move")
DispatchQueue.main.async {
self.collectionView!.numberOfItems(inSection: 0)
self.collectionView.moveItem(at: indexPath!, to: newIndexPath!)
}
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
print ("section info changed in fecthed controller")
let indexSet = IndexSet(integer: sectionIndex)
switch type {
case .insert:
self.collectionView!.numberOfItems(inSection: 0)
collectionView.insertSections(indexSet)
break
case .delete:
self.collectionView!.numberOfItems(inSection: 0)
collectionView.deleteSections(indexSet)
case .update, .move:
fatalError("Invalid change type in controller(_:didChange:atSectionIndex:for:). Only .insert or .delete should be possible.")
}
}
func addSaveNotificationObserver() {
removeSaveNotificationObserver()
print ("context onbserver notified")
saveObserverToken = NotificationCenter.default.addObserver(forName: .NSManagedObjectContextObjectsDidChange, object: dataController?.backgroundContext, queue: nil, using: handleSaveNotification(notification:))
}
func removeSaveNotificationObserver() {
if let token = saveObserverToken {
NotificationCenter.default.removeObserver(token)
}
}
func handleSaveNotification(notification:Notification) {
DispatchQueue.main.async {
self.collectionView!.numberOfItems(inSection: 0)
self.collectionView.reloadData()
}
}
4 Réponses :
Je ne peux pas vous dire quel est le problème avec 1), mais je pense que 2) n'est pas (juste) un problème avec la base de données.
L'erreur que vous obtiendrez généralement lorsque vous ajoutez ou supprimez des éléments / des sections à une collectionView, mais lorsque NumberofitemSectionSection em> est appelé ensuite les chiffres ne s'additionnent pas. Exemple: vous avez 5 articles et ajoutez 2, mais NumberofitemSectionSection em> est appelé et renvoie 6, ce qui crée l'incohérence. P> Dans votre cas, je suppose que vous ajouteriez des éléments avec collectionviev.insertitems () em>, mais cette ligne renvoie 0 après: p> DispatchQueue.main.async {
print ("calling section items")
self.collectionView!.numberOfItems(inSection: 0)
self.collectionView.insertItems(at: [newIndexPath!])
}
Merci @robin Bork. Quant à la ligne qui semble déroutante, je l'ai utilisé sur la base de Michael SU République ici: Stackoverflow.com/questions/19199985/... . La discussion indique que le problème provient d'un bogue de la vue de la collection, car il ne connaît pas le nombre d'articles qu'il a, l'ajout de cette ligne le laisserait savoir le compte, mais il semble que cela ne résoudrait pas complètement le problème. J'ai imprimé "ImagesCount", pour les 3 premières opérations d'insertion, il est toujours 1, tout à coup, il a sauté à 4, après cela, l'insert produit l'accident. Une idée de cette idée?
Ah, c'est bon à savoir, je n'étais pas au courant de ce bogue. En ce qui concerne votre problème: cela pourrait être causé par une condition de course lors de l'ajout de données à la DB en arrière-plan, mais il est difficile de dire sans l'essayer, donc c'est juste une supposition. Ma recommandation est à 1) Déplacez tout le code DB hors de la VC pour une architecture de nettoyeuse et 2) cache les éléments une autre modification (insérer / supprimer) à une propriété afin qu'il existe une "vérité" centrale pour le contenu de votre Collectionview à tout moment. À l'heure actuelle, chaque méthode de rappel met son propre appel à la DB et nous ne savons pas si le résultat est toujours le même
Une autre observation: je vois que vous avez du code pour ajouter et supprimer des sections dans votre vue de collection. Est-ce que c'est appelé? Dans votre numéros de choix, vous ne faites aucune distinction entre les différentes sections, autant que je sache, cela pourrait également entraîner des chiffres erronés.
Merci pour l'aide. Oui, vous avez raison sur les mises à jour de la section qui n'était pas nécessaire. Je pourrais enfin résoudre les deux problèmes, j'ai posté une réponse à ce sujet, vous pouvez le vérifier. Merci encore
Vous avez un problème commun avec uicollectionview code> incohérences lors de la mise à jour par lots.
Si vous effectuez une suppression / l'ajout de nouveaux éléments dans l'ordre incorrect
uicollectionview code> pourrait crash.
Ce problème a 2 solutions typiques: p>
Le problème est NsfetchedResultSluTroller ne doit utiliser qu'un thread principal Uicollectionview sera mis à jour correctement une fois que les données sont enregistrées dans le Writecontext Strong> avec la chaîne suivante: p>
Writecontext (fil d'arrière-plan) -> readcontext (fil principal) -> NsfetchedResultController (fil principal) -> Uicollectionview (fil principal) P>
blockQuote> let writecontext = nsmanagedObjectContext (ConcurrencyType: .privateQueconCurencype)
Laissez readcontext = nsmaniedObjectContext (ConcurrencyType: .MainQueconCurencyCurencyType)
Laissez FetchedController = NsfetchedResultStructeur (Fetchrequest: Demande, GestionedObjectContext: ReadContext, SectionNameKathey: Nil, Cachename: Nil)
writecontext.parent = readcontext code> p>
Je voudrais remercier Je pourrais enfin résoudre les deux problèmes. P> Pour le problème de la collectionView, je me sentais comme si j'étais mis à jour trop de fois, comme vous pouvez le constater dans le code, je l'ai utilisé pour la mise à jour dans deux en plus de cela, CollectionView a un bogue dans la maintenance des articles dans une section parfois comme Le problème était dans l'objet image, je créais un objet sur le contexte code> (code> (qui ne contient aucune donnée insérée), obtenir son identifiant, puis construire un objet correspondant sur le Si quelqu'un a une autre réflexion différente de ce que j'ai dit sur pourquoi la première méthode crée d'abord un objet d'image vide sur le contexte code>, puis Crée un correspondant sur le contexte d'arrière-plan FetchedRésultsTRoller code> Méthodes de délégation, et également à travers un observateur qui observe tout changement sur le contexte. J'ai donc supprimé tout cela et simplement utiliser cette méthode: p>
indexpathique spécifique p> p> < p> pour le problème d'objet pendling. Comme vous pouvez le constater à partir du code, j'avais un objet de localisation et un objet d'image. Mon objet d'emplacement était déjà rempli d'un emplacement et venait de
Contexte Code>, alors j'ai juste besoin de créer un objet correspondant à partir de celui-ci à l'aide de son identifiant (comme vous le voyez dans le code de la question). p>
contexte d'arrière-plan code>. Après avoir lu sur cette erreur et en pensant à mon code, je pensais que la raison peut-être parce que l'objet d'image sur le contexte code> ne contient aucune donnée. Donc, j'ai supprimé le code qui crée cet objet sur le contexte code> CoD> et a créé un directement sur le contexte d'arrière-plan code> et l'a utilisé comme dans le code ci-dessous, et cela a fonctionné! p>
code> n'a pas fonctionné, s'il vous plaît laissez-nous savoir. p> p>