Skip to content

Commit f7afbef

Browse files
Fixes NSInternalInconsistencyException when multiple sections are updated in a table/collection view
1 parent 631eb6e commit f7afbef

File tree

1 file changed

+65
-39
lines changed

1 file changed

+65
-39
lines changed

ReactiveUI/Cocoa/CommonReactiveSource.cs

+65-39
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,18 @@ public IObservable<IEnumerable<NotifyCollectionChangedEventArgs>> DidPerformUpda
152152
readonly ISubject<IEnumerable<NotifyCollectionChangedEventArgs>> didPerformUpdates =
153153
new Subject<IEnumerable<NotifyCollectionChangedEventArgs>>();
154154

155+
/// <summary>
156+
/// While updating sections, updates from all sections should be batched together
157+
/// while invoking PerformBatchUpdates. sectionUpdatesSubject emits updates from
158+
/// each section, which are buffered to get updates from all sections before making
159+
/// call to PerformBatchUpdates.
160+
/// </summary>
161+
/// <value>List of tuples with section index, changes and an IEnumerable containing
162+
/// change indexes
163+
/// </value>
164+
Subject<Tuple<int, NotifyCollectionChangedEventArgs, IEnumerable<int>>> sectionUpdatesSubject =
165+
new Subject<Tuple<int, NotifyCollectionChangedEventArgs, IEnumerable<int>>>();
166+
155167
public CommonReactiveSource(IUICollViewAdapter<TUIView, TUIViewCell> adapter)
156168
{
157169
this.adapter = adapter;
@@ -287,7 +299,7 @@ void resetupAll(IReadOnlyList<TSectionInfo> newSectionInfo)
287299
disp2.Add(current
288300
.Changed
289301
.Select(timestamped)
290-
.Buffer(TimeSpan.FromMilliseconds(250), RxApp.MainThreadScheduler)
302+
.Buffer(TimeSpan.FromMilliseconds(125), RxApp.MainThreadScheduler)
291303
.Where(b => b.Count > 0)
292304
.Subscribe(
293305
xs => sectionCollectionChanged(section, xs),
@@ -301,6 +313,52 @@ void resetupAll(IReadOnlyList<TSectionInfo> newSectionInfo)
301313
adapter.ReloadData();
302314
}));
303315

316+
disp.Add(sectionUpdatesSubject
317+
.Buffer(TimeSpan.FromMilliseconds(125), RxApp.MainThreadScheduler)
318+
.Where(updates => updates.Count > 0)
319+
.Subscribe(updates => {
320+
List<NotifyCollectionChangedEventArgs> eas = new List<NotifyCollectionChangedEventArgs>();
321+
322+
this.Log().Debug("Beginning update");
323+
adapter.PerformBatchUpdates(() => {
324+
foreach (var update in updates.AsEnumerable().Reverse()) {
325+
var ea = update.Item2;
326+
var section = update.Item1;
327+
var changeAction = ea.Action;
328+
var changedIndexes = update.Item3;
329+
eas.Add(ea);
330+
331+
switch (changeAction) {
332+
case NotifyCollectionChangedAction.Add:
333+
doUpdate(adapter.InsertItems, changedIndexes, section);
334+
break;
335+
case NotifyCollectionChangedAction.Remove:
336+
doUpdate(adapter.DeleteItems, changedIndexes, section);
337+
break;
338+
case NotifyCollectionChangedAction.Replace:
339+
doUpdate(adapter.ReloadItems, changedIndexes, section);
340+
break;
341+
case NotifyCollectionChangedAction.Move:
342+
// NB: ReactiveList currently only supports single-item
343+
// moves
344+
345+
this.Log().Debug("Calling MoveRow: {0}-{1} => {0}{2}", section, ea.OldStartingIndex, ea.NewStartingIndex);
346+
347+
adapter.MoveItem(
348+
NSIndexPath.FromRowSection(ea.OldStartingIndex, section),
349+
NSIndexPath.FromRowSection(ea.NewStartingIndex, section));
350+
break;
351+
default:
352+
this.Log().Debug("Unknown Action: {0}", changeAction);
353+
break;
354+
}
355+
}
356+
}, () => {
357+
this.Log().Debug("Ending update");
358+
didPerformUpdates.OnNext(eas);
359+
});
360+
}));
361+
304362
this.Log().Debug("Done resetuping all bindings!");
305363
}
306364

@@ -339,8 +397,8 @@ void sectionCollectionChanged(int section, IList<Timestamped<NotifyCollectionCha
339397
return;
340398
}
341399

342-
var updates = eas.Select(ea => Tuple.Create(ea, getChangedIndexes(ea))).ToList();
343-
var allChangedIndexes = updates.SelectMany(u => u.Item2).ToList();
400+
var updates = eas.Select(ea => Tuple.Create(section, ea, getChangedIndexes(ea))).ToList();
401+
var allChangedIndexes = updates.SelectMany(u => u.Item3).ToList();
344402

345403
// Detect if we're changing the same cell more than
346404
// once - if so, issue a reset and be done
@@ -356,7 +414,7 @@ void sectionCollectionChanged(int section, IList<Timestamped<NotifyCollectionCha
356414
.ToList(),
357415
eas[0].NewStartingIndex);
358416

359-
updates = new List<Tuple<NotifyCollectionChangedEventArgs, IEnumerable<int>>>() { Tuple.Create(newEa, getChangedIndexes(newEa)) };
417+
updates = new List<Tuple<int, NotifyCollectionChangedEventArgs, IEnumerable<int>>>() { Tuple.Create(section, newEa, getChangedIndexes(newEa)) };
360418
eas = new List<NotifyCollectionChangedEventArgs>() { newEa };
361419
} else {
362420
this.Log().Debug("Detected a dupe in the changelist. Issuing Reset");
@@ -367,41 +425,9 @@ void sectionCollectionChanged(int section, IList<Timestamped<NotifyCollectionCha
367425
}
368426
}
369427

370-
this.Log().Debug("Beginning update");
371-
adapter.PerformBatchUpdates(() => {
372-
foreach (var update in updates.AsEnumerable().Reverse()) {
373-
var changeAction = update.Item1.Action;
374-
var changedIndexes = update.Item2;
375-
376-
switch (changeAction) {
377-
case NotifyCollectionChangedAction.Add:
378-
doUpdate(adapter.InsertItems, changedIndexes, section);
379-
break;
380-
case NotifyCollectionChangedAction.Remove:
381-
doUpdate(adapter.DeleteItems, changedIndexes, section);
382-
break;
383-
case NotifyCollectionChangedAction.Replace:
384-
doUpdate(adapter.ReloadItems, changedIndexes, section);
385-
break;
386-
case NotifyCollectionChangedAction.Move:
387-
// NB: ReactiveList currently only supports single-item
388-
// moves
389-
var ea = update.Item1;
390-
this.Log().Debug("Calling MoveRow: {0}-{1} => {0}{2}", section, ea.OldStartingIndex, ea.NewStartingIndex);
391-
392-
adapter.MoveItem(
393-
NSIndexPath.FromRowSection(ea.OldStartingIndex, section),
394-
NSIndexPath.FromRowSection(ea.NewStartingIndex, section));
395-
break;
396-
default:
397-
this.Log().Debug("Unknown Action: {0}", changeAction);
398-
break;
399-
}
400-
}
401-
}, () => {
402-
this.Log().Debug("Ending update");
403-
didPerformUpdates.OnNext(eas);
404-
});
428+
foreach (var update in updates.AsEnumerable().Reverse()) {
429+
sectionUpdatesSubject.OnNext(update);
430+
}
405431
}
406432

407433
static IEnumerable<int> getChangedIndexes(NotifyCollectionChangedEventArgs ea)

0 commit comments

Comments
 (0)