Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-23934] iOS: The first view-children not firing click-events when using run-on-main-thread

GitHub Issuen/a
TypeBug
PriorityCritical
StatusClosed
ResolutionFixed
Resolution Date2016-09-27T04:47:24.000+0000
Affected Version/sRelease 5.4.0, Release 5.5.0
Fix Version/sRelease 6.1.0
ComponentsiOS
Labelschildren, clickevent, ios, run-on-main-thread
Reporternicolomonili
AssigneeHans Knöchel
Created2016-09-21T09:26:11.000+0000
Updated2017-04-12T15:18:50.000+0000

Description

Scenario : - *Tableview* with n *TableViewRow* - Every TableViewRow has a *view container* - Every view container has n children (label / view) Problem : - Only the first children of the view container doesn't fire the *touchstart* event , but fire the *click* event. All the other children fire touchstart and click event. Try to enabled the code commented , and disabled the code just above. I edited an example found on the doc
Ti.UI.setBackgroundColor('#000');
var win = Ti.UI.createWindow({
  backgroundColor: 'black',
  exitOnClose: true,
  fullscreen: false,
  title: 'TableView Demo'
});

// generate random number, used to make each row appear distinct for this example
function randomInt(max){
  return Math.floor(Math.random() * max) + 1;
}

var IMG_BASE = 'https://github.com/appcelerator/titanium_mobile/raw/master/demos/KitchenSink/Resources/images/';
var defaultFontSize = Ti.Platform.name === 'android' ? 16 : 14;

var tableData = [];

for (var i=1; i<=20; i++){
	  
	  var row = Ti.UI.createTableViewRow({
	    className:'forumEvent', // used to improve table performance
	    selectedBackgroundColor:'white',
	    rowIndex:i, // custom property, useful for determining the row during events
	    height:110
	  });
	
	  var testView = Ti.UI.createView({
	  	width : Ti.UI.FILL,
	  	height : Ti.UI.FILL
	  });
	  
	  testView.addEventListener("touchstart" , function(){
	  	Ti.API.info("touchstart event");
	  });
	  testView.addEventListener("click" , function(){
	  	Ti.API.info("click event");
	  });
	  
	  var imageAvatar = Ti.UI.createImageView({
	    image: IMG_BASE + 'custom_tableview/user.png',
	    left:10, top:5,
	    width:50, height:50,
	    borderColor : "red"
	  });

	  var labelUserName = Ti.UI.createLabel({
	    color:'#576996',
	    font:{fontFamily:'Arial', fontSize:defaultFontSize+6, fontWeight:'bold'},
	    text:'Fred Smith ' + i,
	    left:70, top: 6,
	    width:200, height: 30,
	    borderColor : "red"
	  });
	  
	
	  var labelDetails = Ti.UI.createLabel({
	    color:'#222',
	    font:{fontFamily:'Arial', fontSize:defaultFontSize+2, fontWeight:'normal'},
	    text:'Replied to post with id ' + randomInt(1000) + '.',
	    left:70, top:44,
	    width:360,
	    borderColor : "red"
	  });
	  
	
	  var imageCalendar = Ti.UI.createImageView({
	    image:IMG_BASE + 'custom_tableview/eventsButton.png',
	    left:70, bottom: 2,
	    width:32, height: 32,
	    borderColor : "red"
	  });
	  
	
	  var labelDate = Ti.UI.createLabel({
	    color:'#999',
	    font:{fontFamily:'Arial', fontSize:defaultFontSize, fontWeight:'normal'},
	    text:'on ' + randomInt(30) + ' Nov 2012',
	    left:105, bottom:10,
	    width:200, height:20,
	    borderColor : "red"
	  });
	  
	  
	  //imageAvatar not fire touchstart event
	  testView.add(imageAvatar);
	  testView.add(labelUserName);
	  testView.add(labelDetails);
	  testView.add(imageCalendar);
	  testView.add(labelDate);
	 
	  //labelUserName not fire touchstart event
	  /*testView.add(labelUserName);
	  testView.add(imageAvatar);
	  testView.add(labelDetails);
	  testView.add(imageCalendar);
	  testView.add(labelDate);*/

	  
	  row.add(testView);
	  tableData.push(row);
}

var tableView = Ti.UI.createTableView({
  backgroundColor:'white',
  data:tableData
});

win.add(tableView);
win.open();

Attachments

FileDateSize
test.mov2016-09-21T09:32:16.000+00005634196

Comments

  1. Hans Knöchel 2016-09-21

    Looks like an issue with the Ti.UI.ImageView component. Does it only happen when being children inside a TableView or also for independent image-views?
  2. nicolomonili 2016-09-21

    Is not an Imageview problem. try to uncomment this
       //labelUserName not fire touchstart event
       /*testView.add(labelUserName);
       testView.add(imageAvatar);
       testView.add(labelDetails);
       testView.add(imageCalendar);
       testView.add(labelDate);*/
        
    and comment this
       //imageAvatar not fire touchstart event
       testView.add(imageAvatar);
       testView.add(labelUserName);
       testView.add(labelDetails);
       testView.add(imageCalendar);
       testView.add(labelDate);
        
    labelUserName is a label , and have the problem
  3. Hans Knöchel 2016-09-21

    Alright, even better to know. Might some some index-issue then. Will move it to TIMOB as it's a valid issue.
  4. nicolomonili 2016-09-21

    The problem is very serious . There is also the problem with n view inside a main view. The first child of the main view not fire any event!
       var win = Ti.UI.createWindow({
           backgroundColor: 'black'
       });
       
       var viewTest = Ti.UI.createView({
       	width : 200,
       	height : 200,
       	backgroundColor : "red",
       	top : 5
       });
       viewTest.addEventListener("touchstart" , function(e){
       	Ti.API.info("touchstart viewTest");
       });
       viewTest.addEventListener("click" , function(e){
       	Ti.API.info("click viewTest");
       });
       
       var viewTest1 = Ti.UI.createView({
       	width : 50,
       	height : 50,
       	backgroundColor : "blue",
       	left : 10
       });
       
       
       var viewTest2 = Ti.UI.createView({
       	width : 50,
       	height : 50,
       	backgroundColor : "green",
       	right : 10
       });
       
       viewTest.add(viewTest1,viewTest2);
       win.add(viewTest);
       win.open();
       
  5. nicolomonili 2016-09-21

    i can't edit the ticket i have to upload another video test for the second case of the problem
  6. Hans Knöchel 2016-09-21

    I noticed somethine really interesting: If you add the event-listeners after the child-views have been added, everything works well. Investigating ... *EDIT*: This is a run-on-main-thread issue! Disabling it in the tiapp fixes the issue. Will investigate in that direction.
  7. nicolomonili 2016-09-21

    Ok with the property set to false the problem is fix. There is some difference if i put the property false ?
  8. Hans Knöchel 2016-09-25

    You need the flag for running Hyperloop, so we need to fix it anyway. It is scheduled for the next release 6.0.0 now.
  9. Hans Knöchel 2016-09-25

    *Some further investigations*: - The issue is only happening when having run-on-main-thread enabled in the tiapp.xml - The issue is only happening to the very first child of the vier-hierarchy - The issue is not happening when assigning the click-events to the child-views directly - The issue is not happening when the event-listener is added to the view after the child-views have been added - Adding an empty "layer-view" that acts as the first child fixes the issue for all other views (hack / workaround) [~cng] I may need a second pair of eyes on this one. It must be some missing #ifdef TI_USE_KROLL_THREAD somewhere to wrap a certain lock/unlock.
  10. Chee Kiat Ng 2016-09-26

    [~hansknoechel] Just briefly checked but, it looks like the difference is affected here on TiViewProxy.m: https://github.com/appcelerator/titanium_mobile/blob/master/iphone/Classes/TiViewProxy.m#L224 to L245 is executed if *run-on-main-thread*. https://github.com/appcelerator/titanium_mobile/blob/master/iphone/Classes/TiViewProxy.m#L255 to L267 is executed if NOT *run-on-main-thread*. If you observe both chunks of code closely, there's only subtle differences. In fact, I hacked a little and commented out: https://github.com/appcelerator/titanium_mobile/blob/master/iphone/Classes/TiViewProxy.m#L230 The issue is solved when *run-on-main-thread*. Hope that helps.
  11. Hans Knöchel 2016-09-26

    That helps greatly, thanks! Will take a closer look today.
  12. Hans Knöchel 2016-09-26

    Here is the problem:
        pthread_rwlock_wrlock(&childrenLock);
        if(position < 0 || position > [children count]) {
            position = (int)[children count];
        }
        [children insertObject:childView atIndex:position];
        //Turn on clipping because I have child
        [[self view] updateClipping];
        pthread_rwlock_unlock(&childrenLock);
        
    In this example, for the first child, the position is nil instead of 0 (for the child-index of the parent). The next view that is added received the correct index (1) and therefore works. So the locks some kind of lose the value of the position property. Investigating in that direction.
  13. Hans Knöchel 2016-09-26

    So basically what I came up with now is that the threading / read-write-lock caused the problems on the main-thread. I solved the issue by moving the (UI-related) clipping outside the lock that is used to define a unique position. It is necessary to keep this lock to prevent certain race-conditions that can occur. It looks like a solid solution to me and makes sense, since the clipping is not UI-thread related and should therefore not be blocked by the read-write-lock. Please take the below demo-code as well as our KitchenSink to test the general behavior of this PR. It is a *very* affective PR and should be well-tested before merging. PR: https://github.com/appcelerator/titanium_mobile/pull/8432 Demo (adjusted from above):
        var win = Ti.UI.createWindow({
            backgroundColor: 'black'
        });
        
        var viewTest = Ti.UI.createView({
            width: 200,
            height: 200,
            backgroundColor: "red",
            top: 5
        });
        
        viewTest.addEventListener("touchstart", function(e) {
            Ti.API.info("touchstart viewTest");
        });
        
        viewTest.addEventListener("click", function(e) {
            Ti.API.info("click viewTest");
        });
        
        var viewTest1 = Ti.UI.createView({
            width: 50,
            height: 50,
            backgroundColor: "blue",
            left: 10
        });
        
        
        var viewTest2 = Ti.UI.createView({
            width: 50,
            height: 50,
            backgroundColor: "green",
            right: 10
        });
        
        viewTest.add(viewTest1);
        viewTest.add(viewTest2);
        
        win.add(viewTest);
        win.open();
        
  14. Chee Kiat Ng 2016-09-27

    CR and FT passed. Tested on earlier reporter sample code as well. Tested on KitchenSink various events tests as well.
  15. Harry Bryant 2016-11-17

    Verified as fixed, Tested the reporter's sample code, the sample code provided by [~hansknoechel] as well as related test suites in KitchenSink such as Table Views, zIndex & Events Interaction. Tested on Sim and Device. Tested On: iPhone 6 Plus 10.1.1 Device & Simulator Mac OS Sierra (10.12.1) Ti SDK: 6.1.0.v20161116071014 Appc Studio: 4.8.0.201611121409 Appc NPM: 4.2.8 App CLI: 6.1.0-14 Xcode 8.1 Node v4.4.7 *Closing ticket.*

JSON Source