Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-13678] iOS: ListView crashes if items are updated very quickly

GitHub Issuen/a
TypeBug
PriorityMedium
StatusClosed
ResolutionFixed
Resolution Date2014-03-20T18:40:25.000+0000
Affected Version/sn/a
Fix Version/s2014 Sprint 06, 2014 Sprint 06 SDK, Release 3.2.3, Release 3.3.0
ComponentsiOS
LabelsListView, qe-closed-3.2.3, qe-testadded
ReporterIngo Muschenetz
AssigneeVishal Duggal
Created2013-04-26T09:39:01.000+0000
Updated2014-03-20T22:50:40.000+0000

Description

Problem description

When items inside a ListView are updated too quickly, the app crashes with the following error: [INFO] : 2013-04-26 10:35:44.204 TestApp[15385:c07] *** Assertion failure in -[_UITableViewUpdateSupport _setupAnimationsForNewlyInsertedCells], /SourceCache/UIKit_Sim/UIKit-2380.17/UITableViewSupport.m:1145

Steps to reproduce

With the following code, you can reproduce the problem. When clicking on a row, the image changes quicly several times. At some point, the app hangs (no errors shown, but cannot use it anymore) and the assertion failure is shown in console.
var win = Ti.UI.createWindow({backgroundColor: 'white'});

var myTemplate = {
	properties: {
		height: '200dp'
	},
    childTemplates: [
        {
            type: 'Ti.UI.ImageView',
            bindId: 'pic',
            properties: {
                width: '200dp', height: '200dp'
            }
        }
    ]
};

var listView = Ti.UI.createListView({
    templates: { 'template': myTemplate },
    defaultItemTemplate: 'template'
});
var section = Ti.UI.createListSection();

var data = [];
for (var i=0; i<500; i++) {
	data.push({pic: {image: 'http://upload.wikimedia.org/wikipedia/commons/1/13/Facebook_like_thumb.png'}});
}

listView.addEventListener('itemclick', function(e) {
	var source = e.bindId;
	var index = e.itemIndex;
	
	var data = section.getItemAt(index);		

	var ii = 0;
	var animateLike = function () {
		Ti.API.info("ii: " + ii);
		data.pic.image = (ii % 2) ? 'http://upload.wikimedia.org/wikipedia/commons/1/13/Facebook_like_thumb.png' : 'http://upload.wikimedia.org/wikipedia/commons/b/b1/Not_facebook_not_like_thumbs_down.png';
		
		section.replaceItemsAt(index, 1, [data]);
		ii++;
		if (ii >= 100) {
			clearInterval(animator);
			animator = null;
		}
	};
	var animator = setInterval(animateLike, 1);
})

section.setItems(data);
listView.setSections([section]);
win.add(listView);
win.open();

Comments

  1. Vishal Duggal 2013-05-02

    Well two things. If you are not changing the template of the item, please use updateItemAt API of the section. Secondly what exactly are you trying to do? From the current example the thing can simply be accomplished by using the images, repeatCount and duration property of the imageView. Also trying to update every 1 ms is a little extreme. Can we please use a more sensible value. I used a 100ms and it worked fine on the simulator. Might want a larger value for devices.
  2. Vishal Duggal 2013-05-02

    Not exactly sure what the customer is trying to accomplish. Have provided workarounds for the sample attached.
  3. Itay Avtalyon 2013-05-02

    First of all - thanks! What I am trying to do is to start a (40-frame) animation upon click. The image view is part of the template. I couldn't figured away how to make the animation start, so I want the old fashioned way and want to animate by changing the image in short interval to simulate animation. I used 400 ms update rate and it crashed the ListView. Will try to use updateItemAt API and report.
  4. Itay Avtalyon 2013-05-02

    UPDATE: Using the updateItemAt instead of the replaceItemsAt seems to make things a lot better and updating is possible even at intervals of 100 without crash on device. Thanks again!
  5. Itay Avtalyon 2013-05-07

    Another update unfortunately: I have two ListViews (one on top of the other) and the top one crashes with the same error. To mitigate I used a timer to postpone the setItems API call, but it still crashes from time to time.
  6. Davide Cassenti 2013-05-14

    Re-opening, as the same exception is raised using setItems
  7. Vishal Duggal 2013-05-15

    Please attach test code showing crash with setItems.
  8. Mark Mokryn 2013-07-11

    I have also seen cases of ListView instability when updates and scrolling are done rapidly. The code below is of course a bit extreme, but I have seen crashes and freezes of the ListView when being rapidly updated under normal scenarios - to the point where for now I have abandoned them and gone back to ScrollViews, unfortunately. To run this code just use it in place of "list_performance_remote_images.js" in the latest Kitchen Sink. This crashes/freezes reliably in iPhone 6.1 simulator.
       var listView;
       
       function genData (){
       	var baseUrl = 'http://placehold.it/';
       	var imageUrl, lastIndex;
       	for (i=30;i<=60;i++){
       		for (j=30;j<=60;j++){
       			imageUrl = baseUrl+i+'x'+j;
       			if (!(j % 5)) {
       				var data = [{template: 'template', avatar:{image:imageUrl},info1:{text:'Appended Url'+i+j}, 
       					info2:{text:'Appended Url'+i +j},
       					info3:{text:'Appended Url'+i+j},
       					properties:{height: 70}}];
       				listView.sections[0].appendItems(data);
       			} else {
       				lastIndex = listView.sections[0].items.length -1;
       				var lastItem = listView.sections[0].getItemAt(lastIndex);
       				lastItem.info3.text += '\nblah blah';
       				lastItem.info2.text += j;
       				lastItem.info1.text += i;
       				lastItem.properties.height += 20;
       				listView.sections[0].updateItemAt(lastIndex, lastItem);
       			}
       			listView.scrollToItem(0, lastIndex);
       		}
       	}
       }
       
       function setupTest(win){
       	var myTemplate = {
       		//properties: {height:60},
       		childTemplates: [
       		{
       			type: 'Ti.UI.ImageView',
       			bindId: 'avatar',
       			properties: {
       				left: 10,
       				width: 50, height: 50
       			},
       		},
       		{
       			type: 'Ti.UI.Label',
       			bindId: 'info1',
       			properties: {
       				color: 'white',
       				font: { fontFamily:'Arial', fontSize: 13, fontWeight:'bold' },
       				left: 70, top: 5, ellipsize:true,
       				width: Ti.UI.FILL, height: Ti.UI.SIZE
       			},
       		},
       		{
       			type: 'Ti.UI.Label',
       			bindId: 'info2',
       			properties: {
       				color: 'white',
       				font: { fontFamily:'Arial', fontSize: 13, fontWeight:'bold' },
       				left: 70, top: 20, ellipsize:true,
       				width: Ti.UI.FILL, height: Ti.UI.SIZE
       			},
       		},
       		{
       			type: 'Ti.UI.Label',
       			bindId: 'info3',
       			properties: {
       				color: 'white',
       				font: { fontFamily:'Arial', fontSize: 13, fontWeight:'bold' },
       				left: 70, top: 35, ellipsize:true,
       				width: Ti.UI.FILL, height: Ti.UI.SIZE
       			},
       		}
       		]
       	};	
       	
       	var section = Ti.UI.createListSection();
       	listView = Ti.UI.createListView({
       		sections: [section],
       		templates: { 'template': myTemplate },
       		defaultItemTemplate: 'template',
       		backgroundColor: 'black',
       	});
       	listView.appendSection(section);
       	
       	win.add(listView);	
       	genData();
       }
       
       function list_performance_remote(_args) {
       	var win = Ti.UI.createWindow({
       		title:'Remote Image Test',
       		orientationModes:[Ti.UI.PORTRAIT],
       		layout:'vertical'
       	});
       
       	var desc = Ti.UI.createLabel({
       		text:'This is a list View that uses a custom template that holds an imageView and a label.\n'+
       		'The imageView loads remote images. Thank you placehold.it\n'+
       		'Expected performance is a smooth scroll experience.\n'+
       		'On scrolling back and forth in the list view, the right image must be loaded.(since we are reusing views)\n\n'
       	})
       	
       	var button = Ti.UI.createButton({title:'Show Me',top:10});
       	
       	win.add(desc);
       	win.add(button);
       	
       	button.addEventListener('click',function(){
       		win.remove(desc);
       		win.remove(button);
       		setupTest(win);
       	})
       	
       	return win;
       }
       
       module.exports = list_performance_remote;
       
  9. Mark Mokryn 2013-07-12

    Why is the status "resolved"? These are serious bugs, ListView appears very fragile to timing issues.
  10. Oscar Kneib 2013-10-02

    I am also suprised that this issue is marked as 'resolved'. This is a serious problem.
  11. Michael X 2013-10-17

    I've also been running into this instability. What additional info would be helpful beyond the example posted by Mark?
  12. Ingo Muschenetz 2013-10-18

    Reopening based on user comments.
  13. Neville Dastur 2013-12-22

    I have just re-built my app using SDK 3.2.0.GA and I am seeing this assertion failure by just using setItems. Not sure if the insertSectionAt call plays a part. It worked fine in SDK 3.1.3.GA An example of the code that creates the issue is. Note $.list refers to a Alloy XML ListView and _collection is a Alloy collection. myListItem is a template defined in alloy. This is using alloy 1.3.0 on OSX 10.8 iOS 7.0.3
            var models = _collection.models;
            var len = models.length;
        
            for (var i = 0; len > i; i++) {
                var item = models[i];
                var dataitem = {
                    template: "myListItem",
                    properties: { objId: item.get("_id") },
                    title: { text: item.get("name") },
                    subtitle: { text: item.get('sub_name') }
                };
                items.push(dataitem);
            }
            var section = Ti.UI.createListSection({
        	headerTitle: 'A header'
            });
            section.setItems(items);
            $.list.insertSectionAt(0,section);
        
  14. Neville Dastur 2013-12-22

    Just confirmed that is the insertSectionAt call. If I add {animate: false} to the animate properties then the problem is fixed. Would suggest this is changed back to high priority. Mainly as the default is animate = true and so will effect quite a lot of apps
  15. Shreyash Shegaonkar 2014-03-06

    Set timeout to 1000ms while fetching. It is not gonna solve the problem but it will reduce the assertion error count
  16. Sabil Rahim 2014-03-20

    Fixed by PR https://github.com/appcelerator/titanium_mobile/pull/5494 We could not reproduce the original issue in the test case, as the timeout was too low. We could reproduce the crash with a timeout of 30ms and repeated clicking the same item. i.e var animator = setInterval(animateLike, 30); Test code
        var win = Ti.UI.createWindow({backgroundColor: 'white'});
         
        var myTemplate = {
            properties: {
                height: '200dp'
            },
            childTemplates: [
                {
                    type: 'Ti.UI.ImageView',
                    bindId: 'pic',
                    properties: {
                        width: '200dp', height: '200dp'
                    }
                }
            ]
        };
         
        var listView = Ti.UI.createListView({
            templates: { 'template': myTemplate },
            defaultItemTemplate: 'template'
        });
        var section = Ti.UI.createListSection();
         
        var data = [];
        for (var i=0; i<500; i++) {
            data.push({pic: {image: 'http://upload.wikimedia.org/wikipedia/commons/1/13/Facebook_like_thumb.png'}});
        }
         
        listView.addEventListener('itemclick', function(e) {
            var source = e.bindId;
            var index = e.itemIndex;
             
            var data = section.getItemAt(index);        
         
            var ii = 0;
            var animateLike = function () {
                Ti.API.info("ii: " + ii);
                data.pic.image = (ii % 2) ? 'http://upload.wikimedia.org/wikipedia/commons/1/13/Facebook_like_thumb.png' : 'http://upload.wikimedia.org/wikipedia/commons/b/b1/Not_facebook_not_like_thumbs_down.png';
                 
                section.replaceItemsAt(index, 1, [data]);
                ii++;
                if (ii >= 100) {
                    clearInterval(animator);
                    animator = null;
                }
            };
            var animator = setInterval(animateLike, 30);
        })
         
        section.setItems(data);
        listView.setSections([section]);
        win.add(listView);
        win.open();
        
  17. Sabil Rahim 2014-03-20

    3_2_X Backport PR : https://github.com/appcelerator/titanium_mobile/pull/5496
  18. Olga Romero 2014-03-20

    Tested and verified the fix with: Appcelerator Studio, build: 3.2.3.201403190645 Titanium SDK, build: 3.2.3.v20140319174101 Xcode 5.1 Node.JS Version: v0.10.13 NPM Version: 1.3.2 ├── acs@1.0.14 ├── alloy@1.3.1 ├── npm@1.3.2 ├── titanium@3.2.1 └── titanium-code-processor@1.1.0 Device: iPhone5S iOS 7.1 Scrolling the list view up and down, no crash

JSON Source