-
Notifications
You must be signed in to change notification settings - Fork 46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changeset produces bad edit steps, crashes UICollectionView. #12
Comments
Do you need to commit each step? Are you doing all in one edit block? |
All edits are performed in |
Have you tried to do each step in its own performBatchUpdates? Or won't it make a difference? |
You don't need to run them through a collection view to see that they are wrong. Just try the edits yourself and you'll see they don't produce the target. Or just look at the first two edits of the first example. |
@bwhiteley, I have drafted an extension on A couple of questions:
Thanks, |
The problem may be with the order in which you do your edits, @bwhiteley, or maybe
Back to your comment:
OK, let’s check it out and play back the edits in the order dictated by Apple’s documentation, first the deletions:
Note that step 2 is the deletion part of the move. We will do the insertion part in step 5 below. In the resulting array, we now do the insertions and replacements:
In both table views and collection views, there are move commands. I would expect that to “just work” like the individual steps above, but I haven’t checked. But maybe that’s what causes the problem. In case one doesn’t want to use moves, I could easily add a function argument to suppress the reduction of delete/insert edits of the same value. I have been been thinking about that for a while already. |
Thanks for looking into this. I don't think the order matters inside My UICollectionView extension is similar to yours. extension UICollectionView {
/// Run this within `performBatchUpdates`
public func applyEdits<T: Equatable> (edits: [Edit<T>],
numberOfOldSections:Int, numberOfNewSections:Int,
indexPathTransform:(index:Int) -> NSIndexPath) {
var deletes:[NSIndexPath] = []
var inserts:[NSIndexPath] = []
var moves:[(NSIndexPath, NSIndexPath)] = []
var reloads:[NSIndexPath] = []
if numberOfNewSections > numberOfOldSections {
let range = NSRange(location: numberOfOldSections, length: numberOfNewSections - numberOfOldSections)
self.insertSections(NSIndexSet(indexesInRange: range))
} else if numberOfNewSections < numberOfOldSections {
let range = NSRange(location: numberOfNewSections, length: numberOfOldSections - numberOfNewSections)
self.deleteSections(NSIndexSet(indexesInRange: range))
}
edits.forEach { edit in
switch edit.operation {
case .Deletion:
deletes.append(indexPathTransform(index: edit.destination))
case .Insertion:
inserts.append(indexPathTransform(index: edit.destination))
case let .Move(origin):
let originIndexPath = indexPathTransform(index: origin)
let destinationIndexPath = indexPathTransform(index: edit.destination)
if originIndexPath.section >= numberOfNewSections || destinationIndexPath.section >= numberOfOldSections {
// UICollectionView does not allow moves from deleted
// sections or moves to inserted sections.
// https://github.com/wtmoose/TLIndexPathTools/issues/18
deletes.append(originIndexPath)
inserts.append(destinationIndexPath)
} else {
moves.append((indexPathTransform(index: origin), indexPathTransform(index: edit.destination)))
}
case .Substitution:
reloads.append(indexPathTransform(index: edit.destination))
}
}
self.deleteItemsAtIndexPaths(deletes)
self.insertItemsAtIndexPaths(inserts)
self.reloadItemsAtIndexPaths(reloads)
moves.forEach { (old, new) in
self.moveItemAtIndexPath(old, toIndexPath: new)
}
}
} Just for fun I switched to your UICollectionView extension and limited it to one section. Here is a crash: A naive implementation doesn't crash: public static func naiveEditDistance(source s: T, target t: T) -> [Edit<T.Generator.Element>] {
var rv:[Edit<T.Generator.Element>] = []
for (oldIndex, item) in s.enumerate() {
guard let newIndex = t.indexOf(item) else {
rv.append(Edit(.Deletion, value:item, destination:oldIndex))
continue
}
let newIndexI = t.startIndex.distanceTo(newIndex)
if newIndexI != oldIndex {
rv.append(Edit(.Move(origin: oldIndex), value:item, destination:newIndexI))
}
}
for (newIndex, item) in t.enumerate() {
if !s.contains(item) {
rv.append(Edit(.Insertion, value:item, destination:newIndex))
}
}
return rv
} |
Can confirm that the original change set generation does cause crashes. The naive implementation from the comment before works reliably. |
@osteslag any thoughts or updates on the issue? |
@andreyz, I’ll start squeezing in some time this week. Unfortunately this collides with a major project I’m finishing up a work. You are welcome to work on the problem in the meantime, of course. Even just identifying where exactly the problem lies would speed things up. These are some of the questions I start searching for answers to:
@bwhiteley’s “naïve” approach has the answer to the latter, I suspect. |
I have made a quick-fix for the problem that you can use until I can take the time to fully understand what’s happening and document the solution. It’s committed in 9fed7bb:
|
@bwhiteley, @andreyz: This issue is now fixed in the v1.0.4 release. |
I have a couple of examples of edit steps that are incorrect.
'64927513' -> '917546832':
delete 4 at index 1
replace with 1 at index 1
replace with 4 at index 4
move 6 from index 0 to 5
insert 8 at index 6
insert 2 at index 8
UICollectionView error: attempt to delete and reload the same index path (path = 0 - 1)
'8C9A2574361B' -> '897A34B215C6':
replace with 3 at index 4
delete 5 at index 5
move 7 from index 6 to 2
replace with B at index 6
replace with 2 at index 7
replace with 5 at index 9
move C from index 1 to 10
insert 6 at index 11
UICollectionView error: attempt to perform a delete and a move from the same index path (path = 0 - 6)
The text was updated successfully, but these errors were encountered: