Titanium Community Questions & Answer Archive

We felt that 6+ years of knowledge should not die so this is the Titanium Community Questions & Answer Archive

Alphabetical scroll bar

Does anyone know how to create an alphabetical scroll bar on the right side of a TableView on the iphone (and possibly android) like what is on the iphone's contacts screen?

— asked August 3rd 2010 by Joseph Yancey
  • iphone
  • mobile
  • scroll
  • tableview
0 Comments

6 Answers

  • Hi Joseph

    Apologies, I have had to edit this post because I had missed a couple of important lines….

    to configure what you want, you need to insert an "index" object in your tableview row for the start of each alphabetical section.

    I'm going to show a couple of more advanced techniques here and like any such solution, its full of different ways in which you could do things. This is just my way.

    So if I have a database that contains the list I want displayed, then I would do as follows.

    First write the database function to retrieve the list. I separate the database functions from the rest. To do this I would put this in a file dbfunctions.js

    var db = (function() {
      var api     = {};
      var conn     = Titanium.Database.open('mydb');
    
      api.all_item_names = function() {
        if (debug){  Titanium.API.info('api.all_item_names'); }
        var results = [];
        var resultSet = conn.execute('select * from items' );
        while ( resultSet.isValidRow()) {
          results.push({
            item_name:  resultSet.fieldByName('item_name')
          });
          resultSet.next();
        }
        resultSet.close();
        return results;
      };
    return api;
    }());
    

    Next the code to display the list in the tableview. First we include the database function, then we set a few variables that we are going to use to store the data and then we retrieve the list using our database function above. I would put the rest of the code in a new file e.g. listitems.js

    Titanium.include('dbfunctions.js');
    
    var tvrow;
    var curheader    = 'A'; //the first letter of the list.
    var list    = [];
    var index    = [];
    var isAndroid   = (Titanium.Platform.name == 'android');
    
    list = ( db.all_item_names() );
    

    We then need to cycle through the retrieved list from start to finish.

    Our little bit of 'magic' to add the list on the right hand side is create an index based upon the curheader variable. If the curheader value is not the same as the first character of the list item, then we
    1) set the curheader variable to the new value;
    2) add it to our createTableViewRow for this item in the list with "header: curheader"; and
    3) we push the new curheader onto our index array. The index array simply contains that curheader value and the position in our list where that value occurs.

    Otherwise we just create a new TableViewRow without the header and do not change the index array.

    for ( var ipos=0; ipos<ilist.length;ipos++){
      if( list[ipos].item_name[0] != curheader){
        curheader = list[ipos].item_name[0];
        tvrow = Titanium.UI.createTableViewRow({
          height:    20, 
          header:    curheader });
        index.push({
          title:     curheader,
          index:     ipos });
      } else {
        tvrow = Titanium.UI.createTableViewRow({height:20 });
      }
    

    Next we add the list item to the new TableViewRow

      var title= Titanium.UI.createLabel({ 
        left:    5, 
        top:     2, 
        height:  '15', 
        color:   '#000', 
        font: {
          fontSize:    11, 
          fontWeight:  'normal',    
          fontFamily:  (isAndroid?'sans-serif':'Helvetica Neue')
        }, 
        text:    list[ipos].item_name });
        tvrow.add(title);
        data.push(tvrow);
      }
    

    Then we populate the Tableview with the data array we have created and add to it the index which we created.

    var tableView = Titanium.UI.createTableView({
      data:            data,
      index:           index
      separatorStyle:  Titanium.UI.iPhone.TableViewStyle.GROUPED,
      top:             1,
      width:           300
    });
    
    win.add(tableView);
    

    You will see that I did not set the first index to "A". The reason for this is that if the first character is a number, it will come up at the top of the list.

    Thats it, corrections included. Hope this helps you.
    Greg

    — answered August 14th 2010 by Gregor Munro
    permalink
    1 Comment
    • Thanks! This example has really helped me understand tableViews and the use of "index". There are a couple of typo's in the example. In the "for loop" it should be "var ipos=0; ipos<list.length;ipos++" – i.e. var should be "list" not "ilist". The variable "data" is used before it is defined and this causes a Javascript execution error. This var could be defined in the section that "list" and "index" are defined – i.e. "var data = [];"

      — commented February 14th 2011 by Andy Felong
  • If anyone is interested, I came up with a nice custom Alphabetical scrollbar that allows you to style your Table however you want and not be stuck using Table Groups.

    It works very similar to the iPhone Contacts listing, where it updates the table position on touchMove.

    This has been tested on Titanium 1.7.5 and iPhone 5.0.

    var model = [  {  Title:'Alphabits' }, {  Title:'Biscuits' }, {  Title:'Cake' }, {  Title:'Candy' }, {  Title:'Green Beans' }, {  Title:'Oranges' },   {  Title:'Pasta' },  {  Title:'Tangerines' },   {  Title:'Trix' },     {  Title:'Zucchini Bread' }  ];
    
    var table = Ti.UI.createTableView({
      backgroundColor: '#fff',
      width:320,
      rowHeight:100
    });
    win.add(table);
    
    var alphaTable = Ti.UI.createTableView({
      separatorStyle:Ti.UI.iPhone.TableViewSeparatorStyle.NONE,
      opacity:0.65,
      scrollable:false,
      right:5,
      top:5,
      width:28,
      height:355,
      borderRadius:13,
      backgroundColor:"#000"
    });
    // alpha table must be on top of other table
    win.add(alphaTable);
    
    
    
    
    var curheader = '0';
    var index = [];
    var rows = [];   
    var alphaRowCount = 0;
    
    // loop through model to add rows to table
    for (var i = 0, l = model.length; i < l; i++) 
    {
    
      // first determine how many rows there will be
      // use substring to get only first letter
      if( model[i].Title.substring(0,1) != curheader)
      {
        curheader = model[i].Title.substring(0,1);
        alphaRowCount++;
      }
    
       var row = Ti.UI.createTableViewRow({ title:model[i].Title });
       rows.push( row );
    }
    
    // reset curheader var
    curheader = '0'; 
    
    // determine Row height by dividing by Alpha Table height
    var rowHeight = 355/alphaRowCount;
    
    // now loop through model again to actually add the alpha rows
    for (var i = 0, l = model.length; i < l; i++) 
    {  
    
      if( model[i].Title.substring(0,1) != curheader)
      {
        curheader = model[i].Title.substring(0,1);
        var alphaRow = Ti.UI.createTableViewRow({
            color:'#fff',
            width:28,
            backgroundColor:"transparent",
            backgroundSelectedColor:"transparent",
            selectedBackgroundColor: "transparent"
        });
    
        alphaRow.height = rowHeight;
    
        // define IndexRow for main Table
        alphaRow.indexRow = i;
    
        var alphaLabel = Ti.UI.createLabel({
              top:0, 
              width:28,
              left: 0,
              font: {  fontSize: 10, fontFamily: 'Helvetica Neue' },
              color: '#fff',
              textAlign: 'center'
        });
        alphaLabel.text = curheader;
        alphaLabel.height = rowHeight;
        alphaRow.add( alphaLabel );
    
        index.push( alphaRow );
      }
    }
    
    table.setData(  rows );
    alphaTable.setData( index );
    
    
    var previousIndex = 0;
    
    alphaTable.addEventListener('touchmove', function(e)
    {
        // subtract from global point to find position
        var newPointY = e.globalPoint.y - 70;
    
        // determine the index
        var newIndex = Math.ceil( newPointY/rowHeight) - 1;
    
      // scroll to index on main Table, and make sure its on the TOP position
      // to make sure it doesnt jiggle, check to make sure newIndex is not the same as previousIndex
      if( newIndex != previousIndex )
      {
        table.scrollToIndex(newIndex,{animated:true,position:Ti.UI.iPhone.TableViewScrollPosition.TOP});
        previousIndex = newIndex;
      }
    });
    
    — answered January 19th 2012 by Mark Goldsmith
    permalink
    8 Comments
    • hi Mark

      Thanks i have just tried it, just need to add first line

      var win = Ti.UI.createWindow();
      

      and at the bottom

      win.open();
      

      and then works perfectly by just copy pasting in app.js

      Nice example very helpful.. i will try in android too wish it work in that too.

      Regards

      Nikunj

      — commented January 19th 2012 by Nikunj Sakhrelia
    • I realized I had to make a slight modification. I also added the app.js code to demonstrate how it works in there.

      I cant seem to delete my other post so here it is:

      // this sets the background color of the master UIView (when there are no windows/tab groups on it)
      Titanium.UI.setBackgroundColor('#000');
      
      // create tab group
      var tabGroup = Titanium.UI.createTabGroup();
      
      
      //
      // create base UI tab and root window
      //
      var win = Titanium.UI.createWindow({  
          title:'Tab 1',
          backgroundColor:'#fff'
      });
      var tab1 = Titanium.UI.createTab({  
          icon:'KS_nav_views.png',
          title:'Tab 1',
          window:win
      });
      
      
      
      
      
      
      
      var model = [  {  Title:'Alphabits' }, {  Title:'Biscuits' }, {  Title:'Cake' }, {  Title:'Candy' }, {  Title:'Green Beans' }, {  Title:'Oranges' },   {  Title:'Pasta' },  {  Title:'Tangerines' },   {  Title:'Trix' },     {  Title:'Zucchini Bread' }  ];
      
      var table = Ti.UI.createTableView({
        backgroundColor: '#fff',
        width:320,
        rowHeight:100
      });
      win.add(table);
      
      var alphaTable = Ti.UI.createTableView({
        separatorStyle:Ti.UI.iPhone.TableViewSeparatorStyle.NONE,
        opacity:0.65,
        scrollable:false,
        right:5,
        top:5,
        width:28,
        height:355,
        borderRadius:13,
        backgroundColor:"#000"
      });
      // alpha table must be on top of other table
      win.add(alphaTable);
      
      
      
      
      var curheader = '0';
      var index = [];
      var rows = [];   
      var alphaRowCount = 0;
      
      // loop through model to add rows to table
      for (var i = 0, l = model.length; i < l; i++) 
      {
      
        // first determine how many rows there will be
        // use substring to get only first letter
        if( model[i].Title.substring(0,1) != curheader)
        {
          curheader = model[i].Title.substring(0,1);
          alphaRowCount++;
        }
      
         var row = Ti.UI.createTableViewRow({ title:model[i].Title });
         rows.push( row );
      }
      
      // reset curheader var
      curheader = '0'; 
      
      // determine Row height by dividing by Alpha Table height
      var rowHeight = 355/alphaRowCount;
      
      var rowIndexID = [];
      
      // now loop through model again to actually add the alpha rows
      for (var i = 0, l = model.length; i < l; i++) 
      {  
      
        if( model[i].Title.substring(0,1) != curheader)
        {
          curheader = model[i].Title.substring(0,1);
          var alphaRow = Ti.UI.createTableViewRow({
              color:'#fff',
              width:28,
              backgroundColor:"transparent",
              backgroundSelectedColor:"transparent",
              selectedBackgroundColor: "transparent"
          });
      
          alphaRow.height = rowHeight;
      
          // define IndexRow for main Table
          rowIndexID.push( { id: i });
      
          var alphaLabel = Ti.UI.createLabel({
                top:0, 
                width:28,
                left: 0,
                font: {  fontSize: 10, fontFamily: 'Helvetica Neue' },
                color: '#fff',
                textAlign: 'center'
          });
          alphaLabel.text = curheader;
          alphaLabel.height = rowHeight;
          alphaRow.add( alphaLabel );
      
          index.push( alphaRow );
        }
      }
      
      table.setData(  rows );
      alphaTable.setData( index );
      
      
      var previousIndex = 0;
      
      alphaTable.addEventListener('touchmove', function(e)
      {
          // subtract from global point to find position
          var newPointY = e.globalPoint.y - 70;
      
          // determine the index
          var newIndex = Math.ceil( newPointY/rowHeight) - 1;
      
          var rowIndex = rowIndexID[ newIndex ].id;
      
        // scroll to index on main Table, and make sure its on the TOP position
        // to make sure it doesnt jiggle, check to make sure newIndex is not the same as previousIndex
        if( rowIndex != previousIndex )
        {
          table.scrollToIndex(newIndex,{animated:false,position:Ti.UI.iPhone.TableViewScrollPosition.TOP});
          previousIndex = newIndex;
        }
      });
      
      
      //
      // create controls tab and root window
      //
      var win2 = Titanium.UI.createWindow({  
          title:'Tab 2',
          backgroundColor:'#fff'
      });
      var tab2 = Titanium.UI.createTab({  
          icon:'KS_nav_ui.png',
          title:'Tab 2',
          window:win2
      });
      
      var label2 = Titanium.UI.createLabel({
          color:'#999',
          text:'I am Window 2',
          font:{fontSize:20,fontFamily:'Helvetica Neue'},
          textAlign:'center',
          width:'auto'
      });
      
      win2.add(label2);
      
      
      
      //
      //  add tabs
      //
      tabGroup.addTab(tab1);  
      tabGroup.addTab(tab2);  
      
      
      // open tab group
      tabGroup.open();
      

      — commented January 19th 2012 by Mark Goldsmith
    • And.. made another small mistake.

        if( rowIndex != previousIndex )
        {
          table.scrollToIndex(rowIndex,{animated:false,position:Ti.UI.iPhone.TableViewScrollPosition.TOP});
          previousIndex = newIndex;
        }
      

      — commented January 19th 2012 by Mark Goldsmith
    • I just realized after testing on an actual iPhone device that the alpha table is too narrow to register touch move events it seems. So I just made the alpha table and rows wider, like 50 pixels instead of 28.

      Another option is to listen for click events

        alphaTable.addEventListener('click', function(e)
              {
                  // event data
                  var index = rowIndexID[ e.row ].id;
                table.scrollToIndex(index,{animated:false,position:Ti.UI.iPhone.TableViewScrollPosition.TOP});
      
              });
      

      — commented January 19th 2012 by Mark Goldsmith
    • Also removing the borderRadius on the table seems to help the touch events respond. Very odd.

      — commented January 19th 2012 by Mark Goldsmith
    • Hi Mark

      Yes on device I am not able to get the touch event. But let me try with the last code in the comment.

      Regard

      Nikunj

      — commented January 20th 2012 by Nikunj Sakhrelia
    • Yeah using the click event always seems to work, but the touchmove event only seems to work with either no borderRadius or a borderRadius of 3 or smaller. I've also tried background images which dont seem to work either. Its very strange.

      — commented January 20th 2012 by Mark Goldsmith
    • Hi i'm converting alphabetical scroll view to android devices.I getting problem in ~e.globalPoint.y~ inside touchmove listener.I couldn't recognize what is globalPoint and its Y axis..Could you please explain.Thanks.

      — commented February 19th 2014 by GaneshKumar Balasubramanian
  • I've searched in Kitchen Sink but didn't find an alphabetical scroll bar.
    I'm using KS 1.4 built with Titanium SDK 1.3 (won't work with SDK.14).
    Where could I find this?

    — answered August 13th 2010 by P. C.
    permalink
    0 Comments
  • I'm interested in this too. As far as i know there is nothing in KS for this.

    — answered August 13th 2010 by Dave Devitt
    permalink
    0 Comments
  • Alright, I got a custom alphabetical listing working using Titanium SDK 1.8.0.1 R3.

    The major difference with this code and the one I posted earlier is that I'm using convertPointToView instead of globalPoint which was depreciated.

    The only bummer about this custom alphabetical listing is that nots nearly as fast as the default one. If you slide your finger up and down on the device, its a bit sluggish (aalthough its lightning fast on the simulator).

    Heres the code:

    http://snipt.org/xlOh8

    — answered January 26th 2012 by Mark Goldsmith
    permalink
    0 Comments
  • Check out the KitchenSink, there should be an example in there…

    — answered August 3rd 2010 by John Welch
    permalink
    0 Comments
The ownership of individual contributions to this community generated content is retained by the authors of their contributions.
All trademarks remain the property of the respective owner.