Titanium SDK for iOS: ScrollableView for Displaying Several Pages of Photo Thumbnails

Here’s a quick example of one way to dynamically populate a ScrollableView using Titanium SDK 2.x. I started with an example from this pastebin sample.

The below code displays a horizontally ScrollableView with default iOS page controls. Each page displays 6 photo thumbnails, while the last page displays the remainder (less than 6). This should work on Android, but I haven’t had time to test it yet (famous last words, I know).

SideScrollPagedView() is a CommonJS module (one of the recommended ways to organize code in Titanium).

module.exports = function SideScrollPagedView() {
	
    var OS = Ti.Platform.osname;	

	// in real life this might come from a database or web service or be dynamically populated from a local directory 
	var data = [ { PhotoID: 1, Photo_Title: 'my first photo', Thumb_Path: 'photothumb_1.jpg'},
				 { PhotoID: 2, Photo_Title: 'my second photo', Thumb_Path: 'photothumb_2.jpg'},  		
                 { PhotoID: 3, Photo_Title: 'my third photo', Thumb_Path: 'photothumb_3.jpg'}, 
                 { PhotoID: 4, Photo_Title: 'my fourth photo', Thumb_Path: 'photothumb_4.jpg' }, 
                 { PhotoID: 5, Photo_Title: 'my fifth photo', Thumb_Path: 'photothumb_5.jpg'}, 
                 { PhotoID: 6, Photo_Title: 'my sixth photo', Thumb_Path: 'photothumb_6.jpg'}, 
                 { PhotoID: 7, Photo_Title: 'yeah, 7th photo', Thumb_Path: 'photothumb_7.jpg'}, 
                 { PhotoID: 8, Photo_Title: 'ok, 8th photo', Thumb_Path: 'photothumb_8.jpg'}, 	
                 { PhotoID: 9, Photo_Title: 'this is the 9th photo', Thumb_Path: 'photothumb_9.jpg'}, 
                 { PhotoID: 10, Photo_Title: 'photo number 10', Thumb_Path: 'photothumb_10.jpg'}, 
                 { PhotoID: 11, Photo_Title: 'photo number 11', Thumb_Path: 'photothumb_11.jpg'}, 
                 { PhotoID: 12, Photo_Title: 'photo number 12', Thumb_Path: 'photothumb_12.jpg'},
                 { PhotoID: 13, Photo_Title: 'photo number 13', Thumb_Path: 'photothumb_13.jpg'},
                 { PhotoID: 14, Photo_Title: 'photo number 14', Thumb_Path: 'photothumb_14.jpg'},
                 { PhotoID: 15, Photo_Title: 'photo number 15', Thumb_Path: 'photothumb_15.jpg'},
                 { PhotoID: 16, Photo_Title: 'photo number 16', Thumb_Path: 'photothumb_16.jpg'} ];
	
	//main view 		
	var self = Ti.UI.createView({
		layout: 'vertical',
		width: '100%',
		height: Ti.UI.SIZE
	});			
	
	//you can stack another one of these above or below this one	
	var mainContentBar = Ti.UI.createView({
		layout: 'horizontal',
		height: Ti.UI.SIZE,
		//borderColor: '#CCCCCC',
		//borderRadius: 2
	});
	self.add(mainContentBar);				
   
	var views = []; //array that holds a collection of "pages" (views) to be assigned to ScrollableView's .view property
    var gameData = data; //copy of 'data' array, lets us slice it up per page, while keeping original list in tact	
	var itemsPerPage =  (OS==='ipad') ? 6 : 4; //number of photos to display per page inside ScrollableView 
	var gutterSpace = (OS==='ipad') ? 16 : 9;  
    var halfOfItems = itemsPerPage * 0.5;
	var buttonWidth = (OS==='ipad') ? 204 : 132; 
	var buttonHeight = (OS==='ipad') ? 152 : 99; 		
	var scrollablePgW = (OS==='ipad') ? '644dip' : '273dip'; 
	var scrollablePgH = (OS==='ipad') ? '360dip' : '230dip'; 	
	var numPages = Math.ceil(data.length/itemsPerPage);	//numPages determined by number of times TotalFavorites / itemsPerPage, rounded up for whole pages		
	var itemsOnLastPage = data.length % itemsPerPage;

	var lastInRow1 = (OS==='ipad') ? 2 : 1;
	var lastInRow2 = (OS==='ipad') ? 5 : 3;
	var sidePadding = true;		
 
	for ( var i = 0 ; i < numPages; i++ )
	{
	      var view = createView(views.length);
    		
          //modify itemsPerPage for last page	           
      	  if(i == (numPages-1)) { itemsPerPage = itemsOnLastPage; }
      	 
      	  var perPageData = gameData.splice(0, itemsPerPage);  
       	 
		  //for each set of itemsPerPage (6 or less), add an icon subview to view 	
  		  for(var h = 0 ; h < itemsPerPage; h++) 
  		  {
  		      //remove gutter space from last icon in each row to prevent the parent view (w/ layout:'horizontal') from forcing the next row too early  		  	 
  			 ( h === lastInRow1 || h === lastInRow2 ) ? sidePadding = false : sidePadding = true;  
  			 
  	   	  	 var subview = createSubView( sidePadding );	      
  	   	  	 var item = perPageData[h]; 
  	   	  	   	   	  	 
  	   	  	 var iconItem = Ti.UI.createView({
				backgroundImage: '/images/photos/thumbs/' + item['Thumb_Path'],
				layout : 'composite',
				width : buttonWidth,
				height : buttonHeight,
				left : 0, 
				top : 0,
				visible : true,
				photoData : item //can be used to later grab property values like photoData.Photo_Title
			});			  	   	  	
  	   	  	iconItem.addEventListener('singletap', function(evt) { 				
				this.fireEvent('scrollItem_Clicked', {data : this.photoData	});				
			}); 			

  	   	  	//add additional subviews to iconItem here as needed

  	   	  	subview.add(iconItem);
  	   	  	 			
  			view.add(subview);
  		  }	      
      	   	  
	      views.push(view);
	}

	function createSubView(xval, yval)
	{
		var rightPadding = (OS==='ipad') ? '16dip' : '8dip';
		var btmPadding = (OS==='ipad') ? '17dip' : '8dip'; 
		if(!sidePadNum) {
		     rightPadding = 0;
		}
		
		var view = Ti.UI.createView({			
			width: buttonWidth, 
			height: buttonHeight, 			
			bottom: btmPadding, 
			right: rightPadding,
			//borderColor: '#FF00FF'			
		});		
		
		return view;
	}	

	function createView(i)
	{
		var view = Ti.UI.createView({
			backgroundColor: '#FFFFFF',
			text: i, 
			layout: 'horizontal'
		});
		return view;
	}	
	 	 
	var scrollView = Titanium.UI.createScrollableView({
			top: (OS==='ipad') ? '25dip' : '15dip',
			left: (OS==='ipad') ? '50dip' : '20dip',
			width: scrollablePgW, 
			height: scrollablePgH, 
			//borderColor: '#AACC00', //for debugging		
			views:views,
		  	showPagingControl:true,		
		  	pagingControlColor: '#666666',
	        currentPage:0
	});	 
	
	mainContentBar.add(scrollView);	        	          	
	
	return self;
}

This assumes that the actual thumbnail images are stored under /Resources/images/photos/thumbs/.

ScrollableView and clickable items – avoiding click/swipe conflicts

Notice how the “iconItem.addEventListener(‘singletap’, function(evt){})” is listening for a ‘singletap’ event and NOT a ‘click’ event. I tried using ‘click’ first and ran into the problem of the photo icons being accidentally clicked while I’m trying to swipe (scroll) the ScrollableView.

Usage

To use the above module inside another file, you can do something like this, depending on what you’re doing:

//Application Window Component Constructor
module.exports = function ApplicationWindow() {
     ...	
	//create component instance
	var self = Ti.UI.createWindow({
		layout: 'composite',
		width: Ti.UI.FILL,
		height: Ti.UI.FILL,
		backgroundColor:'#ffffff',
		navBarHidden:true,
		exitOnClose:true
	})
     ...
    var SideScrollPagedView = require('/ui/SideScrollPagedView');
    var sspv = new SideScrollPagedView();
     self.add(sspv);

    return self;
}

The above code assumes that the scrollable photos component .js file is located in this location: /Resources/ui/SideScrollPagedView.js.

The above ApplicationWindow.js component would be called by app.js in the same way it is in the SingleWindow application template that comes with the Titanium 2.x IDE, like so:

//bootstrap and check dependencies
if (Ti.version &lt; 1.8 ) {
	alert('Sorry - this application template requires Titanium Mobile SDK 1.8 or later');
}
else if (Ti.Platform.osname === 'mobileweb') {
	alert('Mobile web is not yet supported by this template');
}
else {	
	...
	
	//require and open top level UI component	
	var AppWin = require('/ui/ApplicationWindow');
	var appWin = new AppWin();
	appWin.open();	
}
 
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s