Problem Description
After a large model update, find that the listview display only shows what was on displayed, and the rest is blank. The scroll bar shows that I have new records, but is displaying blank lines.
If I load a new screen with the same ListView code or flip to another screen and come back, the data display is correct. If I quit and come back in, it is clean and all data is displayed. Is clearly a screen redraw problem I'm having.
Have even done a simple code test version, within my App, and still have the same problem.
Was working perfectly with 3.3.
This same problem is happening in other screens with ListView as well of my App.
STEPS TO TEST:
- Create a simple alloy project.
- Update project with test code - Link:
https://github.com/PeterTippett/ListViewBugTest
- Run on iOS device/simulator
-List view doesn’t refresh/load data (screenshot 1) but update it’s count value. If we run the project again, then data loads as expected (screenshot 2). But when we refresh/load data again, then a visible section of screen only shows data, others can’t (screenshot 3&4).
Screenshot Link:
http://oi57.tinypic.com/ve17rp.jpg
EXPECTED RESULT:
It’s not working as expected.
Extra info
An error appears in Xcode - TiUIListViewProxy.m (marked in x) Thread 1: EXC_BAD_ACCESS(code=1, address=Oxe)
TiUIListViewProxy.m Thread 1: EXC_BAD_ACCESS(code=1, address=Oxe) - marked with xxxx
-(void)processUpdateActions
{
UITableView *tableView = self.listView.tableView;
BOOL removeHead = NO;
while (YES) {
void (^block)(UITableView *) = nil;
pthread_mutex_lock(&_operationQueueMutex);
if (removeHead) {
[_operationQueue removeObjectAtIndex:0];
}
if ([_operationQueue count] > 0) {
block = [_operationQueue objectAtIndex:0];
removeHead = YES;
}
pthread_mutex_unlock(&_operationQueueMutex);
if (block != nil) {
[tableView beginUpdates];
block(tableView);
xxxx [tableView endUpdates];
Block_release(block);
} else {
[self.listView updateIndicesForVisibleRows];
[self contentsWillChange];
return;
}
}
}
{quote}
Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit/UIKit-3318.0.1/UITableView.m:1582
{quote}
Have built a version that repeats this and done a clean rebuilt of Titanium as well downloading all the latest versions.
https://github.com/PeterTippett/ListViewBugTest - sample code that fails
Have attached three screen shots, one is before an update.
http://developer.appcelerator.com/question/178522/listview-redraw-after-a-large-model-update#comment-220169
{quote}
[INFO] : Found Titanium plugin id=ti.alloy version=1.0
[INFO] : Deploy type: development
[INFO] : Building for target: simulator
[INFO] : Building using iOS SDK: 8.0
[INFO] : Building for iOS Simulator: iPhone 6
[INFO] : Building for device family: universal
[INFO] : Minimum iOS version: 6.0
{quote}
We are seeing the same issue where the entire tableview just doesn't load data. The view is blank. Its only happening on iOS 8.0 Ti 3.4.0. Works fine on iOS 7.1 Ti 3.3.0. Is there a date around when this issue will be resolved?
It is important to note that the bug is also present on iOS 7.1 using Titanium 3.4.0.
To reproduce
1. Download the code of the app found here: https://github.com/appcelerator/alloy/tree/master/test/apps/models/binding_listview 2. Go to index.js inside the controllers folder and create a for-loop at line 32 encapsulating the logic, like this3. Run the app.
The problem
The problem is that every time the data model is added to the collection, internally it calls the "setItems" method of the listview section, thus calling it many times (10 in this case) in a row causing an internal race condition.Simpler test case:
To see the problem, create a new classic app and paste this code in app.jsResolving the issue as "Won't fix". Please reopen if needed. The way to solve the issue is to create an array of models and adding that to the collection, instead of adding the one by one. For example, using the code in the comment above:
Versus this:
Disregard my last comment. Adding an array to the collection still calls the "setItems" method per model in that array. This seems to be an Backbone issue where it calls "trigger" on every model that is added to a collection.
I think the solution is to to pass \{silent: true} to each call to Collection.add() then trigger the update at the end.
[~pec1985] to move to ALOY.
I don't believe this to be an Alloy issue, though it may be true that it's easier to reproduce the condition in Alloy. If the code I provided above works around the problem, it indicates that many updates to the listview's data in quick succession is the cause of the blank items. Those same conditions could also happen with Classic code. I'll also point out that "_This seems to be an Backbone issue where it calls "trigger" on every model that is added to a collection._" is a characteristic of Backbone, not Alloy. Perhaps this is a documentation issue ... that you should suppress the add event when adding items to a view-bound collection.
Adding "silence: true" and "trigger('add');" does help with performance, but the issue of calling listView's setItem is still there. This is the generated Alloy code:
The function "__alloyId19" get's called by the backbone event emitter for every model in the collection array, thus calling "setItems" more times than needed and replacing the list view data. In this case it should only be called once. It is definitely better practice to call "trigger('save')" after adding all the models to the collection. If the developer then wants to add more items, we should compare the new array of items with the previous one and either call "insertItemsAt" or "appendItems"
[~penrique] I can't reproduce this. Furthermore, in my tests, the generated function (e.g. __alloyId19") is getting called just once. Tested with Alloy 1.5.1 and 1.7.0-dev, SDK 3.5.0.GA and 4.0.0. Steps: 1. Create a new Alloy project and copy/import the the binding/listview sample app as you describe above. 2. Update index.js like this:
3. Build for the simulator. The app works as expected; I can add as many rows as I want and I don't see the blank items. 4. Create a new Classic app. 5. Copy the contents of the Alloy app's Resources folder to the Classic app's Resources folder (so you can fiddle with generated code) 6. Update the classic app's iphone/alloy/controllers/index.js to add logging statements as follows:
7. Build the classic app for the simulator. Click a button. You'll see 100 "Looping" log messages followed by one "Calling section.setItems()" log message.
I can't reproduce the issue reported. Environment: Ti SDK 4.1.0.GA, CLI: 4.1.0, iOS 8.3 Tried the test code at https://github.com/PeterTippett/ListViewBugTest, the server that hosts the data seems down, modified the function