{ "id": "138270", "key": "ALOY-1248", "fields": { "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false }, "project": { "id": "11113", "key": "ALOY", "name": "Alloy", "projectCategory": { "id": "10400", "description": "Tools for developing applications", "name": "Tooling" } }, "fixVersions": [], "resolution": null, "resolutiondate": null, "created": "2014-10-19T21:48:54.000+0000", "priority": { "name": "High", "id": "2" }, "labels": [ "3.4.0", "TCSupport", "iphone", "listView" ], "versions": [], "issuelinks": [ { "id": "45854", "type": { "id": "10003", "name": "Relates", "inward": "relates to", "outward": "relates to" }, "inwardIssue": { "id": "137166", "key": "AC-1016", "fields": { "summary": "ListView randomly shows blank rows on section.setItems()", "status": { "description": "A resolution has been taken, and it is awaiting verification by reporter. From here issues are either reopened, or are closed.", "name": "Resolved", "id": "5", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false } } } } ], "assignee": { "name": "fmiao", "key": "fmiao", "displayName": "Feon Sua Xin Miao", "active": true, "timeZone": "America/Vancouver" }, "updated": "2015-07-14T20:58:55.000+0000", "status": { "description": "This issue was once resolved, but the resolution was deemed incorrect. From here issues are either marked assigned or resolved.", "name": "Reopened", "id": "4", "statusCategory": { "id": 2, "key": "new", "colorName": "blue-gray", "name": "To Do" } }, "components": [], "description": "h4. Problem Description\r\nAfter 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.\r\nIf 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.\r\nHave even done a simple code test version, within my App, and still have the same problem.\r\n\r\nWas working perfectly with 3.3.\r\n\r\nThis same problem is happening in other screens with ListView as well of my App.\r\n \r\nh4. STEPS TO TEST:\r\n- Create a simple alloy project.\r\n- Update project with test code - Link: https://github.com/PeterTippett/ListViewBugTest\r\n- Run on iOS device/simulator\r\n-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).\r\n\r\nScreenshot Link: http://oi57.tinypic.com/ve17rp.jpg\r\n\r\nh4. EXPECTED RESULT:\r\nIt’s not working as expected.\r\n\r\nh4. Extra info\r\nAn error appears in Xcode - TiUIListViewProxy.m (marked in x) Thread 1: EXC_BAD_ACCESS(code=1, address=Oxe)\r\n\r\nTiUIListViewProxy.m Thread 1: EXC_BAD_ACCESS(code=1, address=Oxe) - marked with xxxx\r\n{code}\r\n-(void)processUpdateActions\r\n{\r\n UITableView *tableView = self.listView.tableView;\r\n BOOL removeHead = NO;\r\n while (YES) {\r\n void (^block)(UITableView *) = nil;\r\n pthread_mutex_lock(&_operationQueueMutex);\r\n if (removeHead) {\r\n [_operationQueue removeObjectAtIndex:0];\r\n }\r\n if ([_operationQueue count] > 0) {\r\n block = [_operationQueue objectAtIndex:0];\r\n removeHead = YES;\r\n }\r\n pthread_mutex_unlock(&_operationQueueMutex);\r\n if (block != nil) {\r\n [tableView beginUpdates];\r\n block(tableView);\r\nxxxx [tableView endUpdates];\r\n Block_release(block);\r\n } else {\r\n [self.listView updateIndicesForVisibleRows];\r\n [self contentsWillChange];\r\n return;\r\n }\r\n }\r\n}\r\n{code}\r\n{quote}\r\nAssertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit/UIKit-3318.0.1/UITableView.m:1582\r\n{quote}\r\nHave built a version that repeats this and done a clean rebuilt of Titanium as well downloading all the latest versions.\r\nhttps://github.com/PeterTippett/ListViewBugTest - sample code that fails\r\n\r\nHave attached three screen shots, one is before an update.\r\nhttp://developer.appcelerator.com/question/178522/listview-redraw-after-a-large-model-update#comment-220169\r\n{quote}\r\n[INFO] : Found Titanium plugin id=ti.alloy version=1.0 \r\n[INFO] : Deploy type: development\r\n[INFO] : Building for target: simulator\r\n[INFO] : Building using iOS SDK: 8.0\r\n[INFO] : Building for iOS Simulator: iPhone 6\r\n[INFO] : Building for device family: universal\r\n[INFO] : Minimum iOS version: 6.0\r\n{quote}\r\n", "attachment": [ { "id": "52051", "filename": "iOS Simulator Screen Shot 19 Oct 2014 4.09.13 pm.png", "author": { "name": "Peter Tippett", "key": "peter tippett", "displayName": "Peter Tippett", "active": true, "timeZone": "Australia/Sydney" }, "created": "2014-10-19T21:48:54.000+0000", "size": 167300, "mimeType": "image/png" }, { "id": "52050", "filename": "iOS Simulator Screen Shot 19 Oct 2014 4.09.43 pm.png", "author": { "name": "Peter Tippett", "key": "peter tippett", "displayName": "Peter Tippett", "active": true, "timeZone": "Australia/Sydney" }, "created": "2014-10-19T21:48:54.000+0000", "size": 130179, "mimeType": "image/png" }, { "id": "52049", "filename": "iOS Simulator Screen Shot 19 Oct 2014 4.09.50 pm.png", "author": { "name": "Peter Tippett", "key": "peter tippett", "displayName": "Peter Tippett", "active": true, "timeZone": "Australia/Sydney" }, "created": "2014-10-19T21:48:54.000+0000", "size": 116916, "mimeType": "image/png" } ], "flagged": false, "summary": "iOS: ListView blank during redraw", "creator": { "name": "Peter Tippett", "key": "peter tippett", "displayName": "Peter Tippett", "active": true, "timeZone": "Australia/Sydney" }, "subtasks": [], "reporter": { "name": "Peter Tippett", "key": "peter tippett", "displayName": "Peter Tippett", "active": true, "timeZone": "Australia/Sydney" }, "environment": "Titanium SDK version 3.4.0.GA, Max OSX 10.10, Alloy 1.5, Xcode 6.0", "closedSprints": [ { "id": 291, "state": "closed", "name": "2015 Sprint 01 SDK", "startDate": "2015-01-03T01:00:00.000Z", "endDate": "2015-01-17T01:00:00.000Z", "completeDate": "2015-01-19T17:24:40.828Z", "originBoardId": 114 }, { "id": 339, "state": "closed", "name": "2015 Sprint 04 Tooling", "startDate": "2015-02-14T02:34:21.061Z", "endDate": "2015-02-28T02:34:00.000Z", "completeDate": "2015-02-28T15:51:32.596Z", "originBoardId": 121 }, { "id": 330, "state": "closed", "name": "2015 Sprint 03 SDK", "startDate": "2015-01-31T01:00:05.245Z", "endDate": "2015-02-14T01:00:00.000Z", "completeDate": "2015-02-16T21:01:51.914Z", "originBoardId": 114 } ], "comment": { "comments": [ { "id": "331583", "author": { "name": "hetalv985@gmail.com", "key": "hetalv985@gmail.com", "displayName": "Hetal", "active": true, "timeZone": "America/Havana" }, "body": "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?", "updateAuthor": { "name": "hetalv985@gmail.com", "key": "hetalv985@gmail.com", "displayName": "Hetal", "active": true, "timeZone": "America/Havana" }, "created": "2014-11-11T16:29:13.000+0000", "updated": "2014-11-11T16:29:13.000+0000" }, { "id": "333207", "author": { "name": "thebrousse", "key": "thebrousse", "displayName": "Christian Brousseau", "active": true, "timeZone": "Europe/Berlin" }, "body": "It is important to note that the bug is also present on iOS 7.1 using Titanium 3.4.0.", "updateAuthor": { "name": "thebrousse", "key": "thebrousse", "displayName": "Christian Brousseau", "active": true, "timeZone": "Europe/Berlin" }, "created": "2014-11-21T14:59:26.000+0000", "updated": "2014-11-21T14:59:26.000+0000" }, { "id": "342667", "author": { "name": "penrique", "key": "penrique", "displayName": "Pedro Enrique", "active": false, "timeZone": "America/Los_Angeles" }, "body": "h4. To reproduce\r\n1. Download the code of the app found here: https://github.com/appcelerator/alloy/tree/master/test/apps/models/binding_listview\r\n2. Go to index.js inside the controllers folder and create a for-loop at line 32 encapsulating the logic, like this\r\n{code}\r\nfor(var i = 0; i < 10; i++) {\r\n // create a model for the listitem\r\n var model = Alloy.createModel('info', e.modelObj);\r\n // add model to collection which will in turn update the UI\r\n info.add(model);\r\n}\r\n{code}\r\n3. Run the app.\r\n\r\nh4. The problem\r\nThe 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.\r\n\r\nh4. Simpler test case:\r\nTo see the problem, create a new classic app and paste this code in app.js\r\n{code}\r\nvar win = Ti.UI.createWindow({\r\n\tbackgroundColor: 'white'\r\n});\r\nvar section1 = Ti.UI.createListSection({\r\n\theaderTitle: 'This Section'\r\n});\r\nvar listView = Ti.UI.createListView({\r\n\tsections: [section1],\r\n\ttop: 40\r\n});\r\nvar btn = Ti.UI.createButton({\r\n\ttop: 0,\r\n\theight: 40,\r\n\ttitle: 'setItems'\r\n});\r\n\r\nwin.add(btn);\r\nwin.add(listView);\r\nwin.open();\r\n\r\nbtn.addEventListener('click', function(){\r\n\tfor(var i = 0; i < 10; i++) {\r\n\t\tsection1.setItems([\r\n\t\t\t{\r\n\t\t\t\tproperties: { title: 'Title 1'}\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\tproperties: { title: 'Title 2'}\r\n\t\t\t}\r\n\t\t]);\r\n\t}\r\n});\r\n{code}", "updateAuthor": { "name": "penrique", "key": "penrique", "displayName": "Pedro Enrique", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2015-02-09T22:15:17.000+0000", "updated": "2015-02-09T22:18:20.000+0000" }, { "id": "342671", "author": { "name": "penrique", "key": "penrique", "displayName": "Pedro Enrique", "active": false, "timeZone": "America/Los_Angeles" }, "body": "Resolving the issue as \"Won't fix\". Please reopen if needed.\r\n \r\nThe 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.\r\n\r\nFor example, using the code in the comment above:\r\n{code}\r\n// THIS IS BAD (inserting models one by one)\r\nfor(var i = 0; i < 10; i++) {\r\n // create a model for the listitem\r\n var model = Alloy.createModel('info', e.modelObj);\r\n // add model to collection which will in turn update the UI\r\n info.add(model);\r\n}\r\n{code}\r\nVersus this:\r\n{code}\r\n// THIS IS GOOD (inserting all the models at the same time)\r\nvar data = [];\r\nfor(var i = 0; i < 10; i++) {\r\n // create a model for the listitem\r\n var model = Alloy.createModel('info', e.modelObj);\r\n // add the model to a javascript array for later use\r\n data.push(model);\r\n}\r\n// add the array of models to collection which will in turn update the UI\r\ninfo.add(data);\r\n{code}", "updateAuthor": { "name": "penrique", "key": "penrique", "displayName": "Pedro Enrique", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2015-02-09T22:30:11.000+0000", "updated": "2015-02-09T22:33:48.000+0000" }, { "id": "342681", "author": { "name": "penrique", "key": "penrique", "displayName": "Pedro Enrique", "active": false, "timeZone": "America/Los_Angeles" }, "body": "Disregard my last comment. Adding an array to the collection still calls the \"setItems\" method per model in that array.\r\n\r\nThis seems to be an Backbone issue where it calls \"trigger\" on every model that is added to a collection. ", "updateAuthor": { "name": "penrique", "key": "penrique", "displayName": "Pedro Enrique", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2015-02-09T23:10:41.000+0000", "updated": "2015-02-09T23:10:41.000+0000" }, { "id": "342774", "author": { "name": "skypanther", "key": "skypanther", "displayName": "Tim Poulsen", "active": true, "timeZone": "America/New_York" }, "body": "I think the solution is to to pass \\{silent: true} to each call to Collection.add() then trigger the update at the end.\r\n\r\n{code}\r\nfor(var i = 0; i < 10; i++) {\r\n // create a model for the listitem\r\n var model = Alloy.createModel('info', e.modelObj);\r\n // add model to collection which will in turn update the UI\r\n info.add(model, {silent: true});\r\n}\r\ninfo.trigger('add');\r\n{code}", "updateAuthor": { "name": "skypanther", "key": "skypanther", "displayName": "Tim Poulsen", "active": true, "timeZone": "America/New_York" }, "created": "2015-02-10T16:08:21.000+0000", "updated": "2015-02-10T16:08:21.000+0000" }, { "id": "343179", "author": { "name": "ingo", "key": "ingo", "displayName": "Ingo Muschenetz", "active": true, "timeZone": "America/Los_Angeles" }, "body": "[~pec1985] to move to ALOY.", "updateAuthor": { "name": "ingo", "key": "ingo", "displayName": "Ingo Muschenetz", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2015-02-13T00:44:22.000+0000", "updated": "2015-02-13T00:44:22.000+0000" }, { "id": "343257", "author": { "name": "skypanther", "key": "skypanther", "displayName": "Tim Poulsen", "active": true, "timeZone": "America/New_York" }, "body": "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.\r\n\r\nI'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.", "updateAuthor": { "name": "skypanther", "key": "skypanther", "displayName": "Tim Poulsen", "active": true, "timeZone": "America/New_York" }, "created": "2015-02-13T13:36:17.000+0000", "updated": "2015-02-13T13:36:17.000+0000" }, { "id": "343621", "author": { "name": "penrique", "key": "penrique", "displayName": "Pedro Enrique", "active": false, "timeZone": "America/Los_Angeles" }, "body": "Adding \"silence: true\" and \"trigger('add');\" does help with performance, but the issue of calling listView's setItem is still there.\r\nThis is the generated Alloy code:\r\n{code}\r\nfunction Controller() {\r\n function __alloyId19(e) {\r\n if (e && e.fromAdapter) return;\r\n var opts = __alloyId19.opts || {};\r\n var models = filterInvites(__alloyId18);\r\n var len = models.length;\r\n var __alloyId14 = [];\r\n for (var i = 0; len > i; i++) {\r\n var __alloyId15 = models[i];\r\n __alloyId15.__transform = doTransform(__alloyId15);\r\n var __alloyId17 = {\r\n // More code here\r\n };\r\n __alloyId14.push(__alloyId17);\r\n }\r\n opts.animation ? $.__views.invites.setItems(__alloyId14, opts.animation) : $.__views.invites.setItems(__alloyId14);\r\n }\r\n{code}\r\nThe 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. \r\n\r\nIt 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\"", "updateAuthor": { "name": "penrique", "key": "penrique", "displayName": "Pedro Enrique", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2015-02-17T21:45:01.000+0000", "updated": "2015-02-17T21:45:01.000+0000" }, { "id": "343941", "author": { "name": "skypanther", "key": "skypanther", "displayName": "Tim Poulsen", "active": true, "timeZone": "America/New_York" }, "body": "[~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.\r\n\r\nSteps:\r\n\r\n1. Create a new Alloy project and copy/import the the binding/listview sample app as you describe above. \r\n2. Update index.js like this:\r\n\r\n{code}\r\nfunction doButtonClick(e) {\r\n\tif (_.isEmpty(e.modelObj)) {\r\n\t\tvar db = Ti.Database.open('_alloy_');\r\n\t\tdb.execute('DELETE FROM info;');\r\n\t\tdb.close();\r\n\t\tinfo.fetch();\r\n\t} else {\r\n\t\t// create a model for the listitem\r\n\t\tfor(var i = 0; i < 100; i++) {\r\n\t\t var model = Alloy.createModel('info', e.modelObj);\r\n\t\t info.add(model, {silent: true});\r\n\t\t}\r\n\t\tinfo.trigger('add');\r\n\t}\r\n}\r\n{code}\r\n3. 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.\r\n4. Create a new Classic app.\r\n5. Copy the contents of the Alloy app's Resources folder to the Classic app's Resources folder (so you can fiddle with generated code)\r\n6. Update the classic app's iphone/alloy/controllers/index.js to add logging statements as follows:\r\n\r\n{code}\r\nfunction __alloyId27(e) {\r\n if (e && e.fromAdapter) return;\r\n var opts = __alloyId27.opts || {};\r\n var models = __alloyId26.models;\r\n var len = models.length;\r\n var __alloyId22 = [];\r\n for (var i = 0; len > i; i++) {\r\n // ***** ADD LOG OUTPUT HERE *****\r\n console.log('Looping: ' + i);\r\n var __alloyId23 = models[i];\r\n __alloyId23.__transform = doTransform(__alloyId23);\r\n var __alloyId25 = {\r\n properties: {\r\n backgroundColor: \"transparent\"\r\n },\r\n template: \"undefined\" != typeof __alloyId23.__transform[\"template\"] ? __alloyId23.__transform[\"template\"] : __alloyId23.get(\"template\"),\r\n title: {\r\n text: \"undefined\" != typeof __alloyId23.__transform[\"title\"] ? __alloyId23.__transform[\"title\"] : __alloyId23.get(\"title\")\r\n },\r\n subtitle: {\r\n text: \"undefined\" != typeof __alloyId23.__transform[\"subtitle\"] ? __alloyId23.__transform[\"subtitle\"] : __alloyId23.get(\"subtitle\")\r\n },\r\n image: {\r\n image: \"undefined\" != typeof __alloyId23.__transform[\"image\"] ? __alloyId23.__transform[\"image\"] : __alloyId23.get(\"image\")\r\n }\r\n };\r\n __alloyId22.push(__alloyId25);\r\n }\r\n // ***** ADD LOG OUTPUT HERE *****\r\n console.log('Calling section.setItems()')\r\n opts.animation ? $.__views.section.setItems(__alloyId22, opts.animation) : $.__views.section.setItems(__alloyId22);\r\n}\r\n{code}\r\n\r\n7. 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. ", "updateAuthor": { "name": "skypanther", "key": "skypanther", "displayName": "Tim Poulsen", "active": true, "timeZone": "America/New_York" }, "created": "2015-02-19T19:55:14.000+0000", "updated": "2015-02-23T14:50:37.000+0000" }, { "id": "357690", "author": { "name": "fmiao", "key": "fmiao", "displayName": "Feon Sua Xin Miao", "active": true, "timeZone": "America/Vancouver" }, "body": "I can't reproduce the issue reported. \r\nEnvironment: Ti SDK 4.1.0.GA, CLI: 4.1.0, iOS 8.3\r\n\r\nTried the test code at https://github.com/PeterTippett/ListViewBugTest, the server that hosts the data seems down, modified the function\r\n{code}\r\nfunction loadinvites(){\r\n\tfor (var i = 0; i < 50; i++) {\r\n\t\tcreateinvite({\r\n\t\t\tid: i,\r\n\t\t\tcompany: 'company',\r\n\t\t\tmodified: '0',\r\n\t\t\tname: 'id -' + i\r\n\t\t});\r\n\t}\r\n}\r\n{code}", "updateAuthor": { "name": "fmiao", "key": "fmiao", "displayName": "Feon Sua Xin Miao", "active": true, "timeZone": "America/Vancouver" }, "created": "2015-07-14T20:58:55.000+0000", "updated": "2015-07-14T20:58:55.000+0000" } ], "maxResults": 15, "total": 15, "startAt": 0 } } }