Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-17147] IOS: ScrollableView: cacheSize property does not work when the view has a child view component

GitHub Issuen/a
TypeBug
PriorityLow
StatusClosed
ResolutionInvalid
Resolution Date2017-05-02T22:07:40.000+0000
Affected Version/sRelease 3.2.3
Fix Version/sn/a
ComponentsiOS
Labelscommunity
ReporterShuo Liang
AssigneeEric Merriman
Created2014-06-12T07:20:02.000+0000
Updated2017-05-02T22:07:40.000+0000

Description

The "cacheSize" property of the ScrollableView allows the developer to define how many "pages" are to be pre-rendered when the ScrollableView is loaded. As the user scrolls, additional pages are pre-redendered as needed. This is useful for complex scrollable views with many items as it increases performance and reduces memory use. Problem: If the pages (views) contains child views, the cacheSize property does not work and all pages are pre-rendered at once. When loading hundreds of complex pages, this results on the ScrollableView becoming very slow as it is not taking advantage of the cache logic. Test case: The code below shows this problem. You can set the TEST_CASE variable between 1 and 4 to test the four different cases. The first two cases load only a single view control (a view, and an imageview). These two cases work, and as can be seen from the console log, only 3 pages are pre-rendered initially, and as you scroll additional pages are pre-rendered as needed. Remote images are loaded as needed as well. The last two cases load views with children (3 = a view with a child imageview, and 4 = a view with a child view). As can be seen from the console, these two cases fail, as cacheSize is ignored and the ScrollableView pre-renders all 100 pages at once. app.js
// window
var win = Ti.UI.createWindow({
    title: 'TEST',
    backgroundColor: '#ffffff'
});
 
 
// returns array of remote images (from kitchensink)
function getPhotos() {
    var data = [];
    var baseUrl = 'http://placehold.it/';
    var imageUrl;
    for (i=30;i<40;i++){
        for (j=30;j<40;j++){
            imageUrl = baseUrl+i+'x'+j;
            data.push(imageUrl);
        }
    }   
    return data;
}
 
 
// load event for remote image
function img_load(e) {
    Ti.API.info('Loaded remote image ' + e.source.id + ': ' +e.source.image);
}
 
 
// postlayout event for view
function view_postlayout(e) {
    Ti.API.info('Post-layout ' + e.source.id);
}
 
 
// scrollable view with set cache size set to 3
var pager = Ti.UI.createScrollableView({
    cacheSize: 3
});
 
 
// assemble and open
win.add(pager);
win.open();
 
 
//
// TEST CASE SELECTOR (1, 2, 3 or 4)
//
var TEST_CASE = 1;
 
 
// simulate 2s delayed xhr call to bind scrollable view with data
setTimeout(function() {
     
    var photos = getPhotos(),
        pages = []; 
     
    // TEST CASE BRANCH
    if (TEST_CASE == 1)
    {
        // CACHESIZE WORKS HERE: ONLY 3 VIEWS ARE LOADED
         
        // load empty container views
        for (var i = 0; i < photos.length; i++)
        {   
            // container view
            var cont = Ti.UI.createView({ id: i, width: 300, height: 300, backgroundColor: 'red' });
            cont.addEventListener('postlayout', view_postlayout);
             
            // add container views to list
            pages.push(cont);
        }       
    }
    else if (TEST_CASE == 2)
    {
        // CACHESIZE WORKS HERE: ONLY 3 IMAGES ARE LOADED
         
        // load imageviews directly (no container view)
        for (var i = 0; i < photos.length; i++)
        {   
            // image
            var img = Ti.UI.createImageView({ id: i, image: photos[i], width: 200, height: 200 });
            img.addEventListener('load', img_load);
             
            // add imageview to list
            pages.push(img);
        }           
    }
    else if (TEST_CASE == 3)
    {
        // CACHESIZE DOES NOT WORK HERE: ALL CONTAINERS AND CHILD REMOTE IMAGES ARE LOADED AT ONCE
         
        // load imageviews inside a container view
        for (var i = 0; i < photos.length; i++)
        {   
            // image view
            var img = Ti.UI.createImageView({ id: i, image: photos[i], width: 200, height: 200 });
            img.addEventListener('load', img_load);
             
            // container view
            var cont = Ti.UI.createView({ id: 'container '+i, width: 300, height: 300, backgroundColor: 'blue' });
            cont.addEventListener('postlayout', view_postlayout);
            cont.add(img);
             
            // add container to list
            pages.push(cont);
        }       
    }
    else if (TEST_CASE == 4)
    {
        // CACHESIZE DOES NOT WORK HERE: ALL CONTAINERS AND CHILD VIEWS ARE LOADED AT ONCE
         
        // load view inside a container view
        for (var i = 0; i < photos.length; i++)
        {   
            // child view
            var child = Ti.UI.createView({ id: 'child '+i, width: 200, height: 200, backgroundColor: 'red' });
            child.addEventListener('postlayout', view_postlayout);
             
            // container view
            var cont = Ti.UI.createView({ id: 'container '+i, width: 300, height: 300, backgroundColor: 'blue' });
            cont.addEventListener('postlayout', view_postlayout);
            cont.add(child);
             
            // add container to list
            pages.push(cont);
        }       
    }
     
    // bind pages to scrollable view
    pager.applyProperties({
        views: pages,
        currentPage: 0
    });
     
     
}, 2000);
Expected result: cacheSize should work regardless if the pages are single controls or composite controls.

Comments

  1. Ed 2014-07-30

    will this be taken on for a fix on 3.3.1? it's a big issue for us that cacheSize is broken since we make extensive use of ScrollableViews on our app to browse content and images. thank you!
  2. Ed 2014-08-29

    I'm still seeing this problem with the v3.4.0GA daily build, as well as v3.3.0. I've narrowed down the test and found the problem seems to be related to when the scrollableview pages are bound: If they are bound before the scrollableview is added to the window, then cacheSize works. If they are bound after the scrollableview has been added to the window, then cacheSize does not work. (This scenario is very common, since data can be pulled from a remote api and then bound to an already rendered scrollableview) Here's another test case to show this. Comment and uncomment the two lines below the two test conditions to see the difference. Test 1 binds the pages (views) before the scrollableview is added to the window and cacheSize works as expected as shown by the console log. Test 2 binds the pages after the scrollableview has been added to the window, and cacheSize is ignored. All thirteen pages are rendered with remote images downloaded when the pages are bound as can be seen by the log. The setTimeout() is there to make sure the pages are bound after the window .add() method, and to simulate pulling data from a remote api to build the pages. I hope this can be fixed in 3.4.0. This is the number one issue that will keep us from publishing, since we make use of scrollable views with _hundreds_ of pages that load remote data. Since cacheSize fails, all remote data is loaded at once, slowing the app, eating up lots of memory and making the app unstable (crashing).
       var win = Ti.UI.createWindow({ backgroundColor : 'white' });
       
       
       var views = [],
       	photos = [
       		'http://upload.wikimedia.org/wikipedia/commons/4/4c/Blackcat-Lilith.jpg',
       		'http://upload.wikimedia.org/wikipedia/commons/thumb/9/9d/Britishblue.jpg/640px-Britishblue.jpg',
       		'http://upload.wikimedia.org/wikipedia/commons/b/bd/Havana_Brown_-_choco.jpg',
       		'http://upload.wikimedia.org/wikipedia/commons/thumb/8/83/Red_Cat_in_Torzhok_City.jpg/1280px-Red_Cat_in_Torzhok_City.jpg',
       		'http://upload.wikimedia.org/wikipedia/commons/thumb/c/c2/Cat_in_Kautokeino.jpg/1280px-Cat_in_Kautokeino.jpg',
       		'http://upload.wikimedia.org/wikipedia/commons/e/e7/Jammlich_crop.jpg',
       		'http://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Kamee01.jpg/800px-Kamee01.jpg',
       		'http://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Tabby_Pfaffengrund.JPG/1280px-Tabby_Pfaffengrund.JPG',
       		'http://upload.wikimedia.org/wikipedia/commons/2/20/Penny_snuggles.JPG',
       		'http://upload.wikimedia.org/wikipedia/commons/6/60/Neighbours_Siamese.jpg',
       		'http://upload.wikimedia.org/wikipedia/commons/thumb/6/6a/Catstalkprey.jpg/800px-Catstalkprey.jpg',
       		'http://upload.wikimedia.org/wikipedia/commons/thumb/2/28/Gato_Barra%C3%B1a_Galicia_2.jpg/1280px-Gato_Barra%C3%B1a_Galicia_2.jpg',
       		'http://upload.wikimedia.org/wikipedia/commons/thumb/3/33/Spielendes_K%C3%A4tzchen.JPG/800px-Spielendes_K%C3%A4tzchen.JPG'
       	];
        
       
       // build pages for scrollable view 
       for (var i = 0; i < photos.length; i++)
       {
       	var 
       		// scrollview photo container
       		sv = Ti.UI.createScrollView({
       			 top: 0
       			,left: 0
       			,right: 0
       			,bottom: 0
       			,showHorizontalScrollIndicator: false
       			,showVerticalScrollIndicator: false
       		}),
       		
       		// remote photo imageview
       		p = Ti.UI.createImageView({
       			image: photos[i],
       		    width: 300,
       		    height: Ti.UI.SIZE,
       			hires: true,
       			autorotate: false
       		});
       	
       	// remote photo events
       	p.addEventListener('load', function(e) { Ti.API.info('Loaded: ' + e.source.image); });
       	p.addEventListener('error', function(e) { Ti.API.info('Error:' + e.source.image); });
       	
       	// add
       	sv.add(p);
       
       	// add to collection
       	views.push(sv);
       }
       
       
       // scrollableview
       var sbv = Ti.UI.createScrollableView({
       	top: 0,
       	left: 0,
       	right: 0,
       	bottom: 0,
       	cacheSize: 3  // <-- ONLY PRE-LOAD 3 VIEWS (IMAGES)
       });
       
       
       Ti.API.info('CHECK COUNT OF LOADED IMAGES BELOW...');
       
       
       /*
        * TEST 1: WORKS
        * BIND VIEWS BEFORE ADDING TO WINDOW.
        * RESPECTS CACHESIZE=3 AND PRE-LOADS ONLY 3 IMAGES
        */
       //sbv.applyProperties({ views: views, currentPage: 6 }); 
       //win.add(sbv);
       	
       
       
       /*
        * TEST 2: DOES NOT WORK!
        * BIND VIEWS AFTER ADDING TO WINDOW.
        * DOES NOT RESPECT CACHESIZE=3 AND LOADS ALL 13 IMAGES AT ONCE
        */
       win.add(sbv); 
       setTimeout(function() { sbv.applyProperties({ views: views, currentPage: 6 }); }, 2000);
       
       
       
       // open window
       win.open();
       
  3. Nuno Costa 2015-12-10

    Hey guys, Is this issue already fixed? Thanks.
  4. nicolomonili 2016-02-22

    Any news about this?
  5. Alberto Marcone 2016-12-01

    same here, this bugs is breaking our application
  6. Lee Morris 2017-05-02

    Resolving ticket as Invalid as there is now a new version of Kitchen Sink available and we no longer support the version which relates to this ticket.

JSON Source