Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-11973] iOS: Animating 'top' value of UITableView causes it stop scrolling

GitHub Issuen/a
TypeBug
PriorityHigh
StatusClosed
ResolutionFixed
Resolution Date2012-12-06T23:06:56.000+0000
Affected Version/sRelease 2.1.4, Release 3.1.0
Fix Version/sRelease 3.1.0, 2012 Sprint 25, 2012 Sprint 25 API
ComponentsiOS
LabelsSupportTeam
ReporterEduardo Gomez
AssigneeIngo Muschenetz
Created2012-12-06T00:32:36.000+0000
Updated2014-06-19T12:42:54.000+0000

Description

Issue

Not able to longer scroll the tableView all the way to the bottom, when attempt animating the 'top' value of a tableView, causes it the contentView to stop scrolling correctly on iOS.

Reproducible steps

* 1. Start the app! :) * 2. Scroll the tableView up/down to make sure that all rows are visible * 3. Click on the blue bar (Where it says 'Click me!') * 4. Attempt to scroll the tableView all the way to the bottom * 5. Click on the blue bar * 6. Attempt to scroll the tableView (after it is now in its original state) This happens after step 4, and then step 6 will return the table to its original state, where you are still unable to scroll to the bottom.

Expected

Scroll tableView as needed

Actual

You can scroll the entire tableView, only once, when actually the application is first launched.

Tested on

iOS 6 simulator iPhone 4 - iOS 6

Console output

[WARN] New layout set while view [object TiUITableView] animating: Will relayout after animation.

Runnable sample

var platformHeight = Ti.Platform.displayCaps.platformHeight - 20, //status bar
	platformWidth = Ti.Platform.displayCaps.platformWidth;

var fullsizeContainer = Ti.UI.createAnimation({height: platformHeight, duration: 300}); 
var halfsizeContainer = Ti.UI.createAnimation({height: platformHeight/2, duration: 300})

var showGreenBar = Ti.UI.createAnimation({top: 60, duration: 300}); 
var hideGreenBar = Ti.UI.createAnimation({top: 20, duration: 300});

var TABLESTATE = {half: 1, full: 2};

(function() {
	
	var appWindow = Ti.UI.createWindow({
		backgroundColor:'white'
	});
	
	var container = Ti.UI.createView({
		backgroundColor: 'white',
		height: platformHeight/2,
		width: platformWidth,
		bottom: 0
	});
	
	var blueBar = Ti.UI.createLabel({
		backgroundColor: 'blue',
		text: "Click Me!",
		color: 'white',
		width: platformWidth,
		height: 20,
		top: 0,
		textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER, 
		borderColor: 'black' //for aesthetics 
	});
	
	var greenBar = Ti.UI.createView({
		backgroundColor: 'green',
		width: platformWidth,
		height: 40,
		top: 20
	});
	
	var tableData = [];

	var sectionOne = Ti.UI.createTableViewSection({
		headerTitle: "Section One"
	});
	
	var sectionTwo = Ti.UI.createTableViewSection({
		headerTitle: "Section Two"
	});
	
	for(var i = 0; i < 20; i++){
		
		var row = Ti.UI.createTableViewRow({
			title: "Example Row"
		});
		
		if(i%2){
			sectionOne.add(row);
		} else {
			sectionTwo.add(row);
		}
	}
	
	tableData.push(sectionOne);
	tableData.push(sectionTwo);
	
	var tableView = Ti.UI.createTableView({
		backgroundColor: 'white',
		height: Ti.UI.FILL,
		width: platformWidth,
		top: 20,
		data: tableData,
		borderColor: 'red'
	});
	
	var tableState = TABLESTATE.half;
	blueBar.addEventListener('click', function(e){
		
		if(tableState === TABLESTATE.half){
			//goes from half --> full
			container.animate(fullsizeContainer);
			tableView.animate(showGreenBar);
			tableState = TABLESTATE.full;
		} else {
			//goes from full --> half
			tableView.animate(hideGreenBar);
			container.animate(halfsizeContainer);
			tableState = TABLESTATE.half;	
		}
	});
	
	
	container.add(blueBar);
	container.add(greenBar);
	container.add(tableView);
	
	appWindow.add(container);
	
	appWindow.open();
	
})();

Attachments

FileDateSize
NativeSample.mov (iPod & iPhone).m4v2012-12-12T02:29:01.000+00001771470
TableViewTest.zip2012-12-12T02:29:01.000+000028835
TitaniumSample_1.mov (iPod & iPhone) 2.m4v2012-12-12T02:29:01.000+00001335642
TitaniumSample_2.mov (iPod & iPhone) 2.m4v2012-12-12T02:29:01.000+00001154298

Comments

  1. Vishal Duggal 2012-12-06

    iOS does not support nesting animations. Need to run animations one after the other. Use sample code below
       var platformHeight = Ti.Platform.displayCaps.platformHeight - 20, //status bar
           platformWidth = Ti.Platform.displayCaps.platformWidth;
        
       var fullsizeContainer = Ti.UI.createAnimation({height: platformHeight, duration: 300}); 
       var halfsizeContainer = Ti.UI.createAnimation({height: platformHeight/2, duration: 300})
        
       var showGreenBar = Ti.UI.createAnimation({top: 60, duration: 300}); 
       var hideGreenBar = Ti.UI.createAnimation({top: 20, duration: 300});
       
        
       var TABLESTATE = {half: 1, full: 2};
        
       (function() {
            
           var appWindow = Ti.UI.createWindow({
               backgroundColor:'white'
           });
            
           var container = Ti.UI.createView({
               backgroundColor: 'white',
               height: platformHeight/2,
               width: platformWidth,
               bottom: 0
           });
            
           var blueBar = Ti.UI.createLabel({
               backgroundColor: 'blue',
               text: "Click Me!",
               color: 'white',
               width: platformWidth,
               height: 20,
               top: 0,
               textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER, 
               borderColor: 'black' //for aesthetics 
           });
            
           var greenBar = Ti.UI.createView({
               backgroundColor: 'green',
               width: platformWidth,
               height: 40,
               top: 20
           });
            
           var tableData = [];
        
           var sectionOne = Ti.UI.createTableViewSection({
               headerTitle: "Section One"
           });
            
           var sectionTwo = Ti.UI.createTableViewSection({
               headerTitle: "Section Two"
           });
            
           for(var i = 0; i < 20; i++){
                
               var row = Ti.UI.createTableViewRow({
                   title: "Example Row "+i
               });
                
               if(i%2){
                   sectionOne.add(row);
               } else {
                   sectionTwo.add(row);
               }
           }
            
           tableData.push(sectionOne);
           tableData.push(sectionTwo);
            
           var tableView = Ti.UI.createTableView({
               backgroundColor: 'white',
               height: Ti.UI.FILL,
               width: platformWidth,
               top: 20,
               data: tableData,
               borderColor: 'red'
           });
            
       	var fullsizeHandler = function() {
         		fullsizeContainer.removeEventListener('complete',fullsizeHandler);
         		tableView.animate(showGreenBar);
       	};
       	
       	var halfsizeHandler = function() {
         		halfsizeContainer.removeEventListener('complete',halfsizeHandler);
         		tableView.animate(hideGreenBar);
       	};
       
       	var tableState = TABLESTATE.half;
           blueBar.addEventListener('click', function(e){
                
               if(tableState === TABLESTATE.half){
                   //goes from half --> full
                   fullsizeContainer.addEventListener('complete',fullsizeHandler);
                   container.animate(fullsizeContainer);
                   tableState = TABLESTATE.full;
               } else {
                   //goes from full --> half
                   halfsizeContainer.addEventListener('complete',halfsizeHandler);
                   container.animate(halfsizeContainer);
                   tableState = TABLESTATE.half;   
               }
           });
            
            
           container.add(blueBar);
           container.add(greenBar);
           container.add(tableView);
            
           appWindow.add(container);
            
           appWindow.open();
            
       })();
       
  2. Eduardo Gomez 2012-12-12

    Understood, the 'Complete' event listener fixes the TableView scrolling issue however animation its not quite the look the developer was attempting to make with those animations. Attaching a native sample that performs the nested Animation:

    Attached files

    - *TableViewTest.zip* native sample project _Animation block_ in ContainerViewController.m
       - (void) toggleTable {
           
           if(half){
             
               [UIView beginAnimations:@"animateFull" context:nil];
               [UIView setAnimationDuration:0.2];
               [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
               
               [[containerView view] setFrame:CGRectMake(0, 0, 320, [[UIScreen mainScreen] bounds].size.height)];
               [blueButton setFrame:CGRectMake(0, 0, 320, 20)];
               [greenBar setFrame:CGRectMake(0, 20, 320, 60)];
               [[tableViewController tableView] setFrame:CGRectMake(0, 80, 320, [[UIScreen mainScreen] bounds].size.height - 80)];
               
               [UIView commitAnimations];
               
               half = false;
          
           } else {
       
               [UIView beginAnimations:@"animateHalf" context:nil];
               [UIView setAnimationDuration:0.2];
               [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
               
               [[containerView view] setFrame:CGRectMake(0, [[UIScreen mainScreen] bounds].size.height/2, 320, [[UIScreen mainScreen] bounds].size.height/2)];
               [blueButton setFrame:CGRectMake(0, [[UIScreen mainScreen] bounds].size.height/2 - 20, 320, 20)];
               [greenBar setFrame:CGRectMake(0, [[UIScreen mainScreen] bounds].size.height/2 + 20, 320, 60)];
               [[tableViewController tableView] setFrame:CGRectMake(0, [[UIScreen mainScreen] bounds].size.height/2, 320, [[UIScreen mainScreen] bounds].size.height/2 -20)];
               
               [UIView commitAnimations];
               
            //   [[containerView view] bringSubviewToFront:[tableViewController tableView]];
               half = true;
           }
       }
       

    Attached videos

    - *TitaniumSample_1.mov (iPod & iPhone) 2.m4v* This is the result where animations are nested, a minimum test case was posted (without 'complete' event listener). See how Warnings are being thrown then tableView stops scrolling. - *TitaniumSample_2.mov (iPod & iPhone) 2.m4v* Result after adding a 'Complete' event listener. Fixes the scrolling issue however animates it different. - *NativeSample.mov (iPod & iPhone).m4v* Result of running a native sample where animations worked nested. Have the look the developer attempts to do.
  3. Vishal Duggal 2012-12-12

    The native code is running a single animation within which two views are animated. The Titanium code is running two animations. Right now we do not support animating two views within a single animation. You can fake it by running the subview animation before the parent view animation and giving the subview view animation a extra time. Try this code instead
       var platformHeight = Ti.Platform.displayCaps.platformHeight - 20, //status bar
           platformWidth = Ti.Platform.displayCaps.platformWidth;
       
       
       var fullsizeContainer = Ti.UI.createAnimation({height: platformHeight, duration: 200}); 
       var halfsizeContainer = Ti.UI.createAnimation({height: platformHeight/2, duration: 200})
       
       //Give the tableview animations a extra time to complete (x5) 
       //why so much? To compensate for JS bridge and layout engine delays
       var showGreenBar = Ti.UI.createAnimation({top: 60, duration: 1000}); 
       var hideGreenBar = Ti.UI.createAnimation({top: 20, duration: 1000});
        
       var TABLESTATE = {half: 1, full: 2};
        
       (function() {
            
           var appWindow = Ti.UI.createWindow({
               backgroundColor:'white'
           });
            
           var container = Ti.UI.createView({
               backgroundColor: 'white',
               height: platformHeight/2,
               width: platformWidth,
               bottom: 0
           });
            
           var blueBar = Ti.UI.createLabel({
               backgroundColor: 'blue',
               text: "Click Me!",
               color: 'white',
               width: platformWidth,
               height: 20,
               top: 0,
               textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER, 
               borderColor: 'black' //for aesthetics 
           });
            
           var greenBar = Ti.UI.createView({
               backgroundColor: 'green',
               width: platformWidth,
               height: 40,
               top: 20
           });
            
           var tableData = [];
        
           var sectionOne = Ti.UI.createTableViewSection({
               headerTitle: "Section One"
           });
            
           var sectionTwo = Ti.UI.createTableViewSection({
               headerTitle: "Section Two"
           });
            
           for(var i = 0; i < 20; i++){
                
               var row = Ti.UI.createTableViewRow({
                   title: "Example Row "+i
               });
                
               if(i%2){
                   sectionOne.add(row);
               } else {
                   sectionTwo.add(row);
               }
           }
            
           tableData.push(sectionOne);
           tableData.push(sectionTwo);
            
           var tableView = Ti.UI.createTableView({
               backgroundColor: 'white',
               height: Ti.UI.FILL,
               width: platformWidth,
               top: 20,
               data: tableData,
               borderColor: 'red'
           });
            
           var tableState = TABLESTATE.half;
           blueBar.addEventListener('click', function(e){
               //Run tableView animation before the container animation. 
               if(tableState === TABLESTATE.half){
                   //goes from half --> full
                   tableView.animate(showGreenBar);
                   container.animate(fullsizeContainer);
                   tableState = TABLESTATE.full;
               } else {
                   //goes from full --> half
                   tableView.animate(hideGreenBar);
                   container.animate(halfsizeContainer);
                   tableState = TABLESTATE.half;   
               }
           });
            
            
           container.add(blueBar);
           container.add(greenBar);
           container.add(tableView);
            
           appWindow.add(container);
            
           appWindow.open();
            
       })();
       
  4. Paras Mishra 2013-01-22

    tableView is scrollable. Verified on : iPhone 5, iOS 6 SDK version: 3.1.0.v20130111163212 CLI version : 3.0.23 OS : MAC OSX 10.7.5 XCode : 4.5.1

JSON Source