Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-15501] iOS: ScrollableView with many child views flicker when changing orientation

GitHub Issuen/a
TypeBug
PriorityMedium
StatusClosed
ResolutionWon't Fix
Resolution Date2014-02-03T23:55:47.000+0000
Affected Version/sRelease 3.1.2, Release 3.1.3
Fix Version/s2014 Sprint 03, 2014 Sprint 03 API
ComponentsiOS
LabelssupportTeam, triage
ReporterAshish Nigam
AssigneeVishal Duggal
Created2013-10-16T09:55:17.000+0000
Updated2017-03-22T18:27:02.000+0000

Description

When using multiple views with scrollable view, flickering effect if observed in iOS7 with 3.1.3.GA. Steps to reproduce: 1: Use the sample code1 to reproduce the issue. 2: Use the code in app.js of a newly created project. 3: Build the project for iOS7 device (Simulator does not reproduce it). 4: check the effect while changing the orientation. Note: sample code 2, does not reproduce the issue. Having less views in container view. **Sample code1 - can reproduce issue**
var headerView = Ti.UI.createView({

	backgroundColor : '#DB2027',
	height : '40dp'
});

var headerTitle = Ti.UI.createLabel({

	text : "Main Window",
	font : {
		fontSize : '20dp',
		fontWeight : 'bold'
	},
	textAlign : Ti.UI.TEXT_ALIGNMENT_CENTER,
	color : '#FFFFFF'

});

headerView.add(headerTitle);

// btn

var leftBtnSetting = Ti.UI.createButton({
	settingsOpen : false,
	left : '5dp',
	height : '15dp',
	style : Ti.UI.iPhone.SystemButtonStyle.PLAIN,
	title : "Ashish"
});

headerView.add(leftBtnSetting);

var rtBtn = Ti.UI.createButton({
	title : "right",
	//color: 'white',
	font : {
		fontSize : '15dp',
	},
	right : '5dp'
});

headerView.add(rtBtn);

// btn end

var win = Ti.UI.createWindow({
	top : 20,
	backgroundColor : "black",
	orientationModes : [Ti.UI.PORTRAIT, Ti.UI.LANDSCAPE_LEFT, Ti.UI.LANDSCAPE_RIGHT]
});

var containerView = Ti.UI.createView({
	backgroundColor : "gray",
	top : 0,
	layout : "vertical"
});

containerView.add(headerView);

//view 1

var surveyView = Ti.UI.createView({
	backgroundColor : '#d0d0d0',
	layout : 'vertical'
});

var surveyNames = ['Check1', 'Check2', 'Check3', 'Check4', 'Check5'];
var surveyUnits = ['(feet)', '(degrees)', '(degrees)', '(feet)', '(deg/100 ft)'];

function createColumnHeader (names, units){
    
    
    var viewWidth = (100/names.length).toString() + '%';
    
    var headerView = Ti.UI.createView({
        height : '60dp',
        layout : 'horizontal',
        backgroundColor : '#CCCCCC'
    });
    
    
    names.forEach(function(e, i){
        
        var view = Ti.UI.createView({
        width : viewWidth,
        height : headerView.height,
        left : '0dp',
        borderWidth : 0.3,
        borderColor : '#444444',
        layout: 'vertical'
        });
    
        var label = Ti.UI.createLabel({
            text : e,
            top : '10dp',
            color : '#333333',
            textAlign : Ti.UI.TEXT_ALIGNMENT_CENTER,
            font : {
                fontSize : '12dp',
                fontWeight : 'bold'
            }
            
        });
        
        label.setHeight(Ti.UI.SIZE);
        
        view.add(label);
        
        if(units)
        {
            var unitLabel = Ti.UI.createLabel({
                text : units[i],
                textAlign : Ti.UI.TEXT_ALIGNMENT_CENTER,
                font : {
                    fontSize : '10dp'
                }
            
            });
            view.add(unitLabel);
        }
        
        //view.setHeight(Ti.UI.SIZE);
    
        headerView.add(view);
    });
    
    //headerView.setHeight(Ti.UI.SIZE);
    return headerView;
    
};

var surveyHeaderView = createColumnHeader(surveyNames, surveyUnits);

surveyView.add(surveyHeaderView);

var rows = [];

var values = [3000, 4000, 5000, 6000, 7000];

function createSurveyPageRow (values){
    
    
    var viewWidth = (100/values.length).toString() + '%';
    
    var tableRow = Ti.UI.createTableViewRow({
        height : '40dp',
        className : 'surveyRow',
        backgroundColor : '#EEEEEE'
    });
    
    var rowView = Ti.UI.createView({
        height : '40dp',
        layout : 'horizontal'
    });
    
    
    values.forEach(function(e){
        
        var view = Ti.UI.createView({
            width : viewWidth,
            height : rowView.height,
            left : '0dp',
            layout: 'vertical'
        });
    
        var label = Ti.UI.createLabel({
            text : e,
            top : '10dp',
            textAlign : Ti.UI.TEXT_ALIGNMENT_CENTER,
            font : {
                fontSize : '12dp'
            }
            
        });
        
        view.add(label);
        
        rowView.add(view);
    });
    
    tableRow.add(rowView);
    return tableRow;
    
};

for ( i = 0; i < 5; i++) {
	rows.push(createSurveyPageRow(values));
}

var tableView = Ti.UI.createTableView({
	data : rows,
});

surveyView.add(tableView);

//view end

// var view1 = Ti.UI.createView({
	// width : Ti.UI.FILL,
	// height : Ti.UI.FILL,
	// backgroundColor : "red"
// });

// var view2 = Ti.UI.createView({
	// width : Ti.UI.FILL,
	// height : Ti.UI.FILL,
	// backgroundColor : "yellow"
// });

createLithologyPageRow = function(data){
    
    //var key = Object.keys(data);
    
    //alert(key.length);
    
    var keys = Object.keys(data); // Get an array of property names for object o
    var values = []; // Store matching property values in this array 
    for(var i = 0; i < keys.length; i++) { // For each index in the array
        var key = keys[i]; // Get the key at that index
        values[i] = data[key]; // Store the value in the values array 
    }
    
    var viewWidth = (100/keys.length).toString() + '%';
    
    
    var tableRow = Ti.UI.createTableViewRow({
        backgroundColor : '#666666',
        className : 'lithologyRow'
    });
    
    var rowView = Ti.UI.createView({
        top : '0dp',
        bottom : '0dp',
        layout : 'horizontal'
    });
    
    
    //keys.forEach(function(e, index){
    for(index=0; index<keys.length ; index++){  
        var view = Ti.UI.createView({
            width : viewWidth,
            top : '0dp',
            left : '0dp',
            bottom : '0dp',
            layout: 'vertical'
        });
    
        var val_obj = values[index];
        var val_obj_keys = Object.keys(values[index]);
        var val = [];
        
        for(var i = 0; i < val_obj_keys.length; i++) { // For each index in the array
            var key = val_obj_keys[i]; // Get the key at that index
            val[i] = val_obj[key]; // Store the value in the values array 
        }
        
        
        for(i=0; i<val.length ; i++)
        {
            var label = Ti.UI.createLabel({
                text : val[i],
                top : '0dp',
                textAlign : Ti.UI.TEXT_ALIGNMENT_CENTER,
                font : {
                    fontSize : '14dp'
                }
                
            });
            
            view.add(label);
        }
        view.setHeight(Ti.UI.SIZE); 
        rowView.add(view);
    };
    
    rowView.setHeight(Ti.UI.SIZE);
    tableRow.add(rowView);
    
    //tableRow.setHeight(Ti.UI.SIZE);
    
    return tableRow;
    
};



var lithologyView = Ti.UI.createView({
		backgroundColor : '#e3e3e3',
		layout : 'vertical'
	});

	var lithologyNames = ['Check1', 'Check2', '%', 'REMARKS'];

	var lithologyHeaderView = createColumnHeader(lithologyNames);

	lithologyView.add(lithologyHeaderView);

	var rows = [];

	var value_row1 = {

		'Check1' : {
			1 : '3000'
		},
		'Check2' : {
			0 : 'Check2A',
			1 : 'Check2B',
			2 : 'Check2C',
			3 : 'Check2D'
		},
		'Check3' : {
			1 : '25%',
			2 : '25%',
			3 : '25%',
			4 : '25%'
		},
		'Remarks' : {
			1 : 'YES'
		}
	};

	var value_row2 = {

		'Check1' : {
			1 : '3000'
		},
		'Check2' : {
			0 : 'Check2A',
			1 : 'Check2B',
			2 : 'Check2C',
			3 : 'Check2D'
		},
		'Check3' : {
			1 : '25%',
			2 : '25%',
			3 : '25%',
			4 : '25%'
		},
		'Remarks' : {
			1 : 'YES'
		}
	};

	var value_row3 = {

		'Check1' : {
			1 : '3000'
		},
		'Check2' : {
			0 : 'Check2A',
			1 : 'Check2B',
			2 : 'Check2C',
			3 : 'Check2D'
		},
		'Check3' : {
			1 : '25%',
			2 : '25%',
			3 : '25%',
			4 : '25%'
		},
		'Remarks' : {
			1 : 'YES'
		}
	};
	
	
    rows.push(createLithologyPageRow(value_row1));
    rows.push(createLithologyPageRow(value_row2));
    rows.push(createLithologyPageRow(value_row3));

    var tableView = Ti.UI.createTableView({
        data : rows,
    });
    rows[0].addEventListener('click', function(e) {

        var win = Titanium.UI.createWindow({
            url : 'ui/handheld/lithology_detail.js',
            backgroundColor : '#FFFFFF',
            top:iOS7 ? 20 : 0,
            statusBarStyle : osStatusBarStyle,
            layout : 'vertical',
            fullscreen : false
         });
        win.open(leftNavAnimation);

    });
    tableView.setHeight(Ti.UI.SIZE);
    
    lithologyView.add(tableView);
    
    var monitorSettingsBtn = Ti.UI.createButton({
        title : 'Monitor Settings',
        //bottom : 10,
        //left : '40%',
    });

    lithologyView.add(monitorSettingsBtn);
    
    monitorSettingsBtn.addEventListener('click', function(e) {
        
        var win = Titanium.UI.createWindow({
            url : 'ui/handheld/monitorSettings.js',
            backgroundColor : '#FFFFFF',
            top:iOS7 ? 20 : 0,
            statusBarStyle : osStatusBarStyle,
            layout : 'vertical',
            fullscreen : false
        });
        win.open(leftNavAnimation);
        
    });

var slableView = Ti.UI.createScrollableView({
	views : [surveyView, lithologyView],
	showPagingControl : true
});

containerView.add(slableView);

win.add(containerView);

win.open();

**sample code2 - does not reproduce issue**
var headerView = Ti.UI.createView({

	backgroundColor : '#DB2027',
	height : '40dp'
});

var headerTitle = Ti.UI.createLabel({

	text : "Main Window",
	font : {
		fontSize : '20dp',
		fontWeight : 'bold'
	},
	textAlign : Ti.UI.TEXT_ALIGNMENT_CENTER,
	color : '#FFFFFF'

});

headerView.add(headerTitle);

// btn

var leftBtnSetting = Ti.UI.createButton({
	settingsOpen : false,
	left : '5dp',
	height : '15dp',
	style : Ti.UI.iPhone.SystemButtonStyle.PLAIN,
	title:"Ashish"
});

headerView.add(leftBtnSetting);

var rtBtn = Ti.UI.createButton({
	title : "right",
	//color: 'white',
	font : {
		fontSize : '15dp',
	},
	right : '5dp'
});

headerView.add(rtBtn);

// btn end

var win = Ti.UI.createWindow({
	top : 20,
	backgroundColor : "black",
	orientationModes : [Ti.UI.PORTRAIT, Ti.UI.LANDSCAPE_LEFT, Ti.UI.LANDSCAPE_RIGHT]
});

var containerView = Ti.UI.createView({
	backgroundColor : "gray",
	top : 0,
	layout : "vertical"
});

containerView.add(headerView);

var view1 = Ti.UI.createView({
	width : Ti.UI.FILL,
	height : Ti.UI.FILL,
	backgroundColor : "red"
});

var view2 = Ti.UI.createView({
	width : Ti.UI.FILL,
	height : Ti.UI.FILL,
	backgroundColor : "yellow"
});

var slableView = Ti.UI.createScrollableView({
	views : [view1, view2],
	showPagingControl : true
});

containerView.add(slableView);

win.add(containerView);

win.open();

Comments

  1. Ashish Nigam 2013-10-16

    Sample code2 does not reproduce the issue, as it does not contain many views attached to a single view of scrollable view.
  2. Vishal Duggal 2014-02-03

    This is not exactly a ScrollableView problem but a symptom of our layout system limitation that the ScrollableView exacerbates (since it has to manage and update the internal page cache on rotation) The way this UI is set up, you have nested vertical layouts in horizontal layouts within vertical layouts. While the horizontal and vertical layouts are nice to have they are inherently slower than the absolute layout system since the layout engine has to measure every view before starting a layout pass. The more constraints the layout engine has to resolve the slower the layout is going to be A second problem is with the Ti.UI.TEXT_ALIGNMENT_CENTER property of the Label. This property is only useful if you have multiple lines of text or the width of the Label is more than that of the text. Setting this property of single lines of text with width set to SIZE only causes UI glitches (the flickering effect) since the actual label drawing happens outside the animated layout loop. This particular screen can easily be modified to an absolute layout. Please see sample code below which modifies the container view and the first page of the Scrollable view to an absolute layout. Further improvements can be gained by incorporating the table header views into the table view itself (as a row so it lays out along with the other rows). The second screen can also be set up similarly.
       
       var headerView = Ti.UI.createView({
           top:'0dp', 
           backgroundColor : '#DB2027',
           height : '40dp'
       });
        
       var headerTitle = Ti.UI.createLabel({
        
           text : "Main Window",
           font : {
               fontSize : '20dp',
               fontWeight : 'bold'
           },
           //textAlign : Ti.UI.TEXT_ALIGNMENT_CENTER,
           color : '#FFFFFF'
        
       });
        
       headerView.add(headerTitle);
        
       // btn
        
       var leftBtnSetting = Ti.UI.createButton({
           settingsOpen : false,
           left : '5dp',
           height : '15dp',
           style : Ti.UI.iPhone.SystemButtonStyle.PLAIN,
           title : "Ashish"
       });
        
       headerView.add(leftBtnSetting);
        
       var rtBtn = Ti.UI.createButton({
           title : "right",
           //color: 'white',
           font : {
               fontSize : '15dp',
           },
           right : '5dp'
       });
        
       headerView.add(rtBtn);
        
       // btn end
        
       var win = Ti.UI.createWindow({
           top : 20,
           backgroundColor : "black",
           orientationModes : [Ti.UI.PORTRAIT, Ti.UI.LANDSCAPE_LEFT, Ti.UI.LANDSCAPE_RIGHT]
       });
        
       var containerView = Ti.UI.createView({
           backgroundColor : "gray"
       });
        
       containerView.add(headerView);
        
       //view 1
        
       var surveyView = Ti.UI.createView({
           backgroundColor : '#d0d0d0'
       });
        
       var surveyNames = ['Check1', 'Check2', 'Check3', 'Check4', 'Check5'];
       var surveyUnits = ['(feet)', '(degrees)', '(degrees)', '(feet)', '(deg/100 ft)'];
        
       function createColumnHeader (names, units){
            
           
           var offset = (100/names.length); 
           var viewWidth = offset.toString() + '%';
            
           var headerView = Ti.UI.createView({
               height : '60dp',
               backgroundColor : '#CCCCCC',
               top:'0dp'
           });
            
           var counter = 0; 
           names.forEach(function(e, i){
                
               var view = Ti.UI.createView({
                   width : viewWidth,
                   height : headerView.height,
                   left : (counter*offset).toString() + '%',
                   borderWidth : 0.3,
                   borderColor : '#444444'
               });
            
               var label = Ti.UI.createLabel({
                   text : e,
                   top : '10dp',
                   color : '#333333',
                   //textAlign : Ti.UI.TEXT_ALIGNMENT_CENTER,
                   font : {
                       fontSize : '12dp',
                       fontWeight : 'bold'
                   }
                    
               });
                
               view.add(label);
                
               if(units)
               {
                   var unitLabel = Ti.UI.createLabel({
                       text : units[i],
                       //textAlign : Ti.UI.TEXT_ALIGNMENT_CENTER,
                       font : {
                           fontSize : '10dp'
                       },
                       top: '30dp'
                    
                   });
                   view.add(unitLabel);
               }
                
               //view.setHeight(Ti.UI.SIZE);
               counter ++;
               headerView.add(view);
           });
            
           //headerView.setHeight(Ti.UI.SIZE);
           return headerView;
            
       };
        
       var surveyHeaderView = createColumnHeader(surveyNames, surveyUnits);
        
       surveyView.add(surveyHeaderView);
        
       var rows = [];
        
       var values = [3000, 4000, 5000, 6000, 7000];
        
       function createSurveyPageRow (values){
            
            
           var offset = (100/values.length); 
           var viewWidth = offset.toString() + '%';
            
           var tableRow = Ti.UI.createTableViewRow({
               height : '40dp',
               className : 'surveyRow',
               backgroundColor : '#EEEEEE'
           });
            
           var rowView = Ti.UI.createView({
               height : '40dp',
           });
            
           var counter=0; 
           values.forEach(function(e){
                
               var view = Ti.UI.createView({
                   width : viewWidth,
                   height : rowView.height,
                   left : (counter*offset).toString() + '%',
                   layout: 'vertical'
               });
            
               var label = Ti.UI.createLabel({
                   text : e,
                   top : '10dp',
                   font : {
                       fontSize : '12dp'
                   }
                    
               });
                
               view.add(label);
               counter ++; 
               rowView.add(view);
           });
            
           tableRow.add(rowView);
           return tableRow;
            
       };
        
       for ( i = 0; i < 5; i++) {
           rows.push(createSurveyPageRow(values));
       }
        
       var tableView = Ti.UI.createTableView({
           data : rows,
           top: '60 dp'
       });
        
       surveyView.add(tableView);
        
       //view end
        
       // var view1 = Ti.UI.createView({
           // width : Ti.UI.FILL,
           // height : Ti.UI.FILL,
           // backgroundColor : "red"
       // });
        
       // var view2 = Ti.UI.createView({
           // width : Ti.UI.FILL,
           // height : Ti.UI.FILL,
           // backgroundColor : "yellow"
       // });
        
       createLithologyPageRow = function(data){
            
           //var key = Object.keys(data);
            
           //alert(key.length);
            
           var keys = Object.keys(data); // Get an array of property names for object o
           var values = []; // Store matching property values in this array 
           for(var i = 0; i < keys.length; i++) { // For each index in the array
               var key = keys[i]; // Get the key at that index
               values[i] = data[key]; // Store the value in the values array 
           }
            
           var viewWidth = (100/keys.length).toString() + '%';
            
            
           var tableRow = Ti.UI.createTableViewRow({
               backgroundColor : '#666666',
               className : 'lithologyRow'
           });
            
           var rowView = Ti.UI.createView({
               top : '0dp',
               bottom : '0dp',
               layout : 'horizontal'
           });
            
            
           //keys.forEach(function(e, index){
           for(index=0; index<keys.length ; index++){  
               var view = Ti.UI.createView({
                   width : viewWidth,
                   top : '0dp',
                   left : '0dp',
                   bottom : '0dp',
                   layout: 'vertical'
               });
            
               var val_obj = values[index];
               var val_obj_keys = Object.keys(values[index]);
               var val = [];
                
               for(var i = 0; i < val_obj_keys.length; i++) { // For each index in the array
                   var key = val_obj_keys[i]; // Get the key at that index
                   val[i] = val_obj[key]; // Store the value in the values array 
               }
                
                
               for(i=0; i<val.length ; i++)
               {
                   var label = Ti.UI.createLabel({
                       text : val[i],
                       top : '0dp',
                       textAlign : Ti.UI.TEXT_ALIGNMENT_CENTER,
                       font : {
                           fontSize : '14dp'
                       }
                        
                   });
                    
                   view.add(label);
               }
               view.setHeight(Ti.UI.SIZE); 
               rowView.add(view);
           };
            
           rowView.setHeight(Ti.UI.SIZE);
           tableRow.add(rowView);
            
           //tableRow.setHeight(Ti.UI.SIZE);
            
           return tableRow;
            
       };
        
        
        
       var lithologyView = Ti.UI.createView({
               backgroundColor : '#e3e3e3',
               layout : 'vertical'
           });
        
       var lithologyNames = ['Check1', 'Check2', '%', 'REMARKS'];
       
       var lithologyHeaderView = createColumnHeader(lithologyNames);
       
       lithologyView.add(lithologyHeaderView);
       
       var rows = [];
       
       var value_row1 = {
       
           'Check1' : {
               1 : '3000'
           },
           'Check2' : {
               0 : 'Check2A',
               1 : 'Check2B',
               2 : 'Check2C',
               3 : 'Check2D'
           },
           'Check3' : {
               1 : '25%',
               2 : '25%',
               3 : '25%',
               4 : '25%'
           },
           'Remarks' : {
               1 : 'YES'
           }
       };
       
       var value_row2 = {
       
           'Check1' : {
               1 : '3000'
           },
           'Check2' : {
               0 : 'Check2A',
               1 : 'Check2B',
               2 : 'Check2C',
               3 : 'Check2D'
           },
           'Check3' : {
               1 : '25%',
               2 : '25%',
               3 : '25%',
               4 : '25%'
           },
           'Remarks' : {
               1 : 'YES'
           }
       };
       
       var value_row3 = {
       
           'Check1' : {
               1 : '3000'
           },
           'Check2' : {
               0 : 'Check2A',
               1 : 'Check2B',
               2 : 'Check2C',
               3 : 'Check2D'
           },
           'Check3' : {
               1 : '25%',
               2 : '25%',
               3 : '25%',
               4 : '25%'
           },
           'Remarks' : {
               1 : 'YES'
           }
       };
        
        
       rows.push(createLithologyPageRow(value_row1));
       rows.push(createLithologyPageRow(value_row2));
       rows.push(createLithologyPageRow(value_row3));
       
       var tableView = Ti.UI.createTableView({
           data : rows,
       });
       tableView.setHeight(Ti.UI.SIZE);
        
       lithologyView.add(tableView);
        
       var monitorSettingsBtn = Ti.UI.createButton({
           title : 'Monitor Settings',
           //bottom : 10,
           //left : '40%',
       });
       
       lithologyView.add(monitorSettingsBtn);
            
        
       var slableView = Ti.UI.createScrollableView({
           views : [surveyView, lithologyView],
           showPagingControl : true,
           top:'40dp'
       });
        
       containerView.add(slableView);
        
       win.add(containerView);
        
       win.open();
       
  3. Lee Morris 2017-03-22

    Closing ticket as the issue will not fix and with reference to the above comments.

JSON Source