Screencasts

Building iPhone Apps with JavaScript using Titanium

Download Files ↓

Show Notes

Wouldn't it be great to build an iPhone application without learning a line of Objective-C? Wouldn't it be great to put to use the skills you've already honed over the years as a web developer?

In this screencast we'll show you how to build an iPhone app using JavaScript. We'll be using Appcelerator's Titanium Mobile to show you how.

Links

What You'll Learn

  • How to build a full featured native iPhone application in JavaScript
  • How to build a TabGroup with tabs
  • How to create a NavigationGroup and push new windows on the stack with TableViews
  • How to create regular and custom TableViewRows with images and labels
  • How to load JSON and JSONP data from the web using HTTPClient
  • How to play SD and HD videos on supported devices by in-code device detection

Update - 30 May 2011

Blip.tv have changed their API so we've pushed an update to the App Store.

To fix the code yourself, all you need to do is change:

...
function getVideoData(jsonData){
  return jsonData[0];
};
...

To:

...
function getVideoData(jsonData){
  return jsonData[0][0];
};
...

Update - 21 June 2011

Blip.tv have changed their API again. To fix use the following code:

...
function getVideoData(jsonData){
  return jsonData[0].Post;
};
...

Script

to explain how to build a native iPhone app using Appcelerator's Titanium Mobile SDK, which allows you to write JavaScript to create native apps for both iOS and Android devices. We're going to build a Screencasts.org iPhone app, but if you wanted to get it running on Android devices, it could be done with a bit of code branching.

A Few Words About Titanium

Appcelerator's Titanium provides a bridge between JavaScript and native Objective-C for iPhone, and native Java for Android. So building apps with Titanium has the benefit of using the familiar JavaScript language, while still getting the 'native' app experience for both iOS and Android platforms. Native applications often feel more responsive than some other HTML and JavaScript wrapper frameworks that are our there. This is where Titanium shines.

We've found Titanium to be really fast and easy for relatively straightforward apps, like the Screencasts app we're going to build today. It will use table views to display data and play videos. But there are other app solutions we prefer for other kinds of mobile app projects, which we'll cover in future episodes.

Appcelerator's Titanium is a fairly young platform, and it's documentation reflects that. Just be aware that you may have to do a bit of digging to find what you need. Their documentation is available at developer.appcelerator.com

Getting Started

To get started, download Titanium at appcelerator.com. You'll need to create a free Appcelerator account, so complete the sign up form when Titanium launches. Titanium will save your login info for when you launch the app in the future.

You'll also need to the install the iOS SDK from developer.apple.com, and you can also download and install the Android SDK by visiting developer.android.com. Install your SDKs as directed, and then launch Titanium. Titanium should recognize your iOS SDK, and you may have to point Titanium to your Android SDK. We had to jump through a few hoops to get Titanium to recognize the Android SDK, so be sure to follow the latest instructions at Appcelerator.com.

Creating Our Project

Let's click 'New Project' in Titanium's top menu, then select 'Mobile' from the 'Project type' pull-down menu. Let's name the project "Screencasts". Next, we need to create an 'app ID'. App IDs are like URLs in reverse. We need to enter "com" dot company name, in our case "secretmonkeyscience", dot app name, which will be "screencasts". Next, we set a directory for our project on our local machine. You can optionally set your URL. Next, set the Titanium SDK version, which you'll usually want to be the latest version. You'll see which mobile platforms are correctly installed on the next line, iPhone and/or Android. Next, click 'Create Project', and you'll see Titanium add "Screencasts" to our projects menu in the left column. We're also given a panel of some basic settings for our app.

First Run

Let's click on "Test & Package" in the top menu bar, then under "Run Emulator", we'll see a blank console screen with "Launch" & "Stop" buttons below it. You'll also see an iPhone or Android Toggle above the console screen, for testing our app in each simulator. We'll test on iPhone, so let's click "Launch", and we'll see Titanium display a few build messages in the console, then the iPhone simulator will launch. You'll see that Titanium has provided a basic app skeleton with two tabs, and when you click on each, the window changes from "I am Window 1" to "I am Window 2". The Navigation bar title also changes. OK, let's click 'Stop' in Titanium, and let's start building our app.

Titanium Project Folder

To follow along with this screencast, you'll need to download several assets including images and support functions. They're available to download in a zip file on Screencasts.org. Be sure to download and extract these into the Resources folder.

Using your text editor of choice, open the folder containing your project files that you specified when creating your app.

We'll be working exclusively inside this "Resources" folder. The app.js file is the root file where you'll want to start writing the javascript necessary to build your app. Inside, you'll see the JavaScript that Titanium used to build the basic app skeleton we saw in the simulator.

Let's Get Started

We'll clear out the placeholder code and start from scratch. First, we'll set the background color for the whole app and create the tab group, which will hold all of our tabs. Our app will use 2 tabs: one for our latest episodes, the other for our topics.

Titanium.UI.setBackgroundColor('#EEE');
var tabGroup = Titanium.UI.createTabGroup();

When writing out many of Titanium's functions or constants, you can substitute the shorter Ti for the full word Titanium. You'll see us use a mixture of both in this screencast.

Next, we need to create a window which will contain our first tab. We'll set the title to be blank, which would have appeared in the navigation bar, and instead set a custom barImage, iphone-bar.png. If we don't put in an empty string as the title for this window, the tab's title that we're about to create would be used by default. We'll also set a custom backgroundImage to show behind our table view.

...
var latestWindow = Titanium.UI.createWindow({
  title:'',
  barImage:'iphone-bar.png',
  backgroundImage:'grid.png'
});

Our first tab is going to show latestWindow which will contain a table view that lists our latest screencasts. We'll create a tab called latestTab, and we'll set the icon to our clock icon, the title to "Latest", and we'll set the window to latestWindow. So, when this tab is clicked, it will show latestWindow. By the way, we're using icons created by Glyphish.com, which are a must-have for iPhone developers.

...
var latestTab = Titanium.UI.createTab({
  icon:'11-clock.png',
  title:'Latest',
  window:latestWindow
});

Now, our second tab is going to be wired up a bit differently. It's going to show a table view with a list of topics, and when you click on a topic, it will drill-down to show all of the screencasts that are tagged with that topic. So to have this drill-down behavior, we'll need to create a window that will contain a navigation group that will then hold all of the sub windows.

First, let's create the window that will contain our navigation group, which will be shown when our 'Topics' tab is tapped. We need to hide this window's navigation bar, since it will be replaced by our navigation group. We're going to add a nice background, so that when we "pull" on the end of our table views, it will be revealed.

...
var navigationWindow = Titanium.UI.createWindow({
  navBarHidden:true,
  backgroundImage:'grid.png'
});

We'll now create our first sub window which will contain the topics table view. Again, we're using a custom background image for the bar, so we need to pass in an empty string for the title.

...
var topicsWindow = Titanium.UI.createWindow({
  barImage:'iphone-topics-bar.png',
  title:''
});

Next, we'll create the navigation group, and push our topicsWindow onto the navigation stack.

...
var nav = Ti.UI.iPhone.createNavigationGroup({window:topicsWindow});

Then, we add() our nav to the navigationWindow.

...
navigationWindow.add(nav);

Next, we'll create our topicsTab, and set navigationWindow as its window, since that's the parent window of our navigation stack. We'll give it the title 'Topics' and a tags icon.

...
var topicsTab = Titanium.UI.createTab({
  window:navigationWindow,
  title:'topics',
  icon:'15-tags.png'
});

Then, we add our two tabs to our tabGroup in the order we want them displayed, and then call open() on the tabGroup to make it visible.

...
tabGroup.addTab(latestTab);  
tabGroup.addTab(topicsTab);  
tabGroup.open();

Topics Table

We're going to build the functionality of the 'topicsTabfirst. Let's create a table view to hold our topics. We'll pass in an empty array ([]) for the tableview'sdataoption. Each entry in the tableview'sdata` array corresponds to a row in the table.

We need to set its backgroundColor to transparent to reveal the nice background image we have on our navigationWindow. backgroundColor usually accepts hex colors, but has the option of being set to transparent, as we're doing here.

...
var topicsTable = Titanium.UI.createTableView({data:[], backgroundColor:'transparent'});

Next we'll add() the topicsTable to topicsWindow.

...
topicsWindow.add(topicsTable);

If you launch the app in the iOS Simulator from within Titanium, you'll now see that our tabs are in place, and our custom navigation bars with images are also in place. The background images are there, but we need to setup our table views and their data.

HTTPClient()

To populate our topics table, we're going to read in data from a JSON feed on Screencasts.org. We'll need to create an instance of Titanium's HTTPClient object. If you look at the documentation for this object, you'll see that it "(mostly) implements the XMLHttpRequest specification." If you've been writing AJAX code, and writing callbacks for 'XHR' requests, you are already familiar with Titianium's HTTPClient. We'll be using 'xhr' in the names of these instance variables, since the HTTPClient is interchangable with XMLHttpRequest.

First up, let's create an XHR object that deals with the topics. We'll call it xhrTopics.

...
var xhrTopics = Titanium.Network.createHTTPClient();

Next, we'll need to write an anonymous function for `onload. Inside this function, we'll add all of the code we want to be executed when data is returned from the server.

...
xhrTopics.onload = function() { }

If we take a quick look at our topics JSON feed, at screencasts.org/topics.json , we'll see that an array of topics is returned. So we'll parse the JSON into a topics variable:

...
xhrTopics.onload = function() {
  var topics = JSON.parse(this.responseText);
};

Next, we'll setup a data array, which we'll use for the table view. Let's make it an empty array, so that when we cycle through our topics, we can push rows on.

...
xhrTopics.onload = function() {
  var topics = JSON.parse(this.responseText);
  var data = [];
};

Now we'll create that loop which will iterate through our topics, and set contents for each table view row:

...
xhrTopics.onload = function() {
   ...
  
  for (var i=0; i < topics.length; i++) {
  
  };
};

Let's create a topic variable for convinience in the loop.

...
xhrTopics.onload = function() {
  ...
  for (var i=0; i < topics.length; i++) {
    var topic = topics[i].topic;
  };
};

Screencasts.org has a custom color for most topics, so we're going to save that to a variable with an inline conditional, or ternary operator. If the topic doesn't have a color, we'll set it to a blue hex value. We're going to use this color to set the row's background color gradient.

...
xhrTopics.onload = function() {
  ...
  for (var i=0; i < topics.length; i++) {
    var topic = topics[i].topic;
    var customColor = topic.color ? topic.color : '#1169ae';
  };
};

Next, we're going to use a function we wrote to get a slightly lighter shade of the topic color, which we'll use in our background gradient. The way this function works isn't particiularly important, but we'll need to add the Ti.include line for this file at the top of our app.js file. We then create our second color variable lighterCustomColor by calling this function, passing in our customColor. This file was included in the download zip file for this screencast on Screencasts.org.

Ti.include('getLighterColor.js');
...
xhrTopics.onload = function() {
  ...
  for (var i=0; i < topics.length; i++) {
    var topic = topics[i].topic;
    var customColor = topic.color ? topic.color : '#1169ae';
    var lighterCustomColor = getLighterColor(customColor);
  };
};

As a quick side note, Ti.include is primarily how you'd organize your apps once they start growing in complexity. We're trying to keep this demo simple, and mostly all in one file, but moving your supporting code into supporting files is usually a good idea.

Next inside this loop, we're going to create the actual table row, passing in several parameters:

...
xhrTopics.onload = function() {
  ...
  for (var i=0; i < topics.length; i++) {
    ...
    
    var row = Ti.UI.createTableViewRow({
      hasChild:true,
      height:80,
      topic:topic,
      title:topic.name,
      fontSize:24,
      color:'#fff',
      selectedBackgroundColor:'#ddd',
      backgroundGradient: {
        type:'linear',
        colors:[customColor,lighterCustomColor],
        startPoint: {x:0, y:0},
        endPoint: {x:0, y:80},
        backFillStart:false
      }
    });
  };
};

hasChild:true adds the indicator arrow to the right side of the row, indicating to the user that tapping on this row "goes somewhere". height sets the row height. topic being set to topic is our way of adding our entire topic object from the JSON into the row object, so we can use it later. This is a valuable point to remember: You can add your own instance variables to Titanium objects like windows and views, just like you would with any other JavaScript object. Were just creating a very basic table row, so we just need to set the title to topic.name and style it with fontSize and color. Then, we'll set the selectedBackgroundColor for the row, which is the color the row's background will turn when tapped. And finally we can set the backgroundGradient going from our customColor to lighterCustomColor vertically.

Then, we'll push our row onto the data array, then cycle back through the loop for each topic object in our topics array.

...
xhrTopics.onload = function() {
  ...
  for (var i=0; i < topics.length; i++) {
    ...
    data.push(row);
  };
};

Once the loop is finished, we can setData to our data array for the topicsTable.

...
xhrTopics.onload = function() {
  ...
  for (var i=0; i < topics.length; i++) {
    ...
    data.push(row);
  };
  topicsTable.setData(data);
};

Then, after the onload function, we can tell our HTTPClient to open() a GET request, passing in our topics JSON URL. Then we call send(), to trigger the request.

...
xhrTopics.open('GET', 'http://screencasts.org/topics.json');
xhrTopics.send();

Let's launch the app again in Titanium, and click on the "Topics" tab, and as you can see, the table view is there with all of our custom styling. Great!

episodesToData Function

In a minute we're going to create a table view that will load when a topic row is pressed. This new table view will show all of the screencasts tagged with that topic. The cells in this new table view will actually look just like the cells in the "latest" table view, so we're going to create a function that takes episodes data from the JSON, and converts it into a data array suitable for use in a table view. We'll call this function episodesToData, and pass in the raw array of episodes.

...
function episodesToData(episodes) {
  var data = [];
  for (var i=0; i < episodes.length; i++) {
    var episode = episodes[i].episode;
    var row = Ti.UI.createTableViewRow({
      hasChild:true,
      height:80,
      backgroundColor:'#fff',
      video_blip_id:episode.video_blip_id
    });
    
    var image = Titanium.UI.createImageView({
      image:"http://screencasts.org/thumbnails/"+episode.slug+"/280x150png",
      left:-155
    });
    row.add(image);
    
    var episodeTitle = Ti.UI.createLabel({
      text:episode.title,
      color:'#666666',
      left: 155,
      font: {
        fontSize: 13
      },
      height: 70,
      width: 135
    });
    row.add(episodeTitle);
    data.push(row);
  };
  return data;
}

A lot of this code should look familiar from the topics table. We're looping through the episodes array, creating table view rows, adding elements to them, then pushing the row onto our data array at the end of each loop. We're adding a variable called video_blip_id to each row, which is a string blip.tv assigns to each of our videos.

Instead of just using the default title attribute when creating our table view row, we're going to create a more customized row.

We're going to add a thumbnail image to the left of the row and a label to the right. We create an image using the 'Titanium.UI.createImageView' command. We pass in the image URL to the image parameter, and set the left parameter to -155, which pushes the image over 155 pixels to the left of center. Next we add() the image to the row.

Now let's create our episodeTitle that will appear on the right. So our text is our episode.title, color a gray color, and then we have some font styling and positioning. Next we add() the episodeTitle label to our row, and push() the row onto our data array.

When the loop is finished running, we then return our data array.

Drill-down table view: Single Topic Table

Now let's move on to creating our single topic table. We'll create two variables that we'll populate later, which will save our topic title and topic color.

...
var singleTopicTitle;
var singleTopicColor;

Next, the createTableView command for our singleTopicTable will look similar to when we created the table view for the available topics.

...
var singleTopicTable = Titanium.UI.createTableView({data: [], backgroundColor: 'transparent'});

Then, we'll create another HTTPClient object, singleTopicXhr, and implement an onload callback for when we get back the single topic's list of episodes as a JSON feed.

    
...
singleTopicXhr.onload = function() {
 var episodes = JSON.parse(this.responseText);
 var data = episodesToData(episodes);
};

In the onload function, we'll parse the JSON we get back into an episodes variable, which is an array. Then, we create our data array for our singleTopicTable by calling episodesToData, passing in our episodes array.

Next, we call a function we created called addEmptyRows, which just helps keep our app looking nice. Since our table view background is set to transparent, we want to make sure that we have at least a full screen of table view rows. This means we want to have at least 5 rows in each table, and this function adds those extra rows as necessary. Let's include this from a supporting file. We'll do this by adding a Ti.include at the top of our app.js file. This addEmptyRows.js was also included in the download accompanying this screencast.

...
Ti.include('addEmptyRows.js');
…
ingleTopicXhr.onload = function() {
 var episodes = JSON.parse(this.responseText);
 var data = episodesToData(episodes);
 
 addEmptyRows(data);
 singleTopicTable.setData(data);
 
 var singleTopicWindow = Titanium.UI.createWindow({
   title: singleTopicTitle,
   barColor: singleTopicColor
 });
 singleTopicWindow.add(singleTopicTable);
 singleTopicWindow.backButtonTitle = "Topics";
 nav.open(singleTopicWindow);
};

We then setData for our singleTopicTable to our data array, and create the singleTopicWindow which will hold our singleTopicTable view. We'll set the title for this window to our singleTopicTitle variable. Finally, we'll set the barColor to our singleTopicColor variable, which makes our navigation bar the screencast topic's color.

Next we add() the singleTopicTable to our singleTopicWindow. Then we set the backButtonTitle to Topics, which will show in the navigation bar so users can go back to the topics table. Finally, we'll push the singleTopicWindow onto our nav navigation stack with the open() method.

Click events

Next, we'll setup the click event listener for our topicsTable, with an anonymous function containing the code we want executed on click, which means 'when a row is tapped'.

...
topicsTable.addEventListener("click", function(e){
 singleTopicTitle = e.row.topic.name;
 singleTopicColor = e.row.topic.color;
 Ti.API.log('http://screencasts.org/topics/'+e.row.topic.slug+'.json');
 singleTopicXhr.open('GET', 'http://screencasts.org/topics/'+e.row.topic.slug+'.json');
 singleTopicXhr.send();
});

The function passes in an event, named e in our case. In the function, we'll set the singleTopicTitle and singleTopicColor variables, then open and send the request via the HTTPClient we just setup, with a custom JSON URL built for the specific topic. We'll build the URL with the variable slug on our topic of the selected row. It read like this, e.row.topic.slug. Remember earlier, we saved each topic in each row. We can now access it again by calling .topic on each row. The slug is just a string we use on our site to build URLs.

We can useTi.API.log to help debug our app. When this click event listener is fired, we can write a log statement to make sure our URL is being built correctly. Ti.API.log will write out information to the Titanium console when you pass in a string or an object.

So, if we launch our app again in Titanium, we'll see that when we tap on a row in our topics table, it correctly opens our new singleTopicTable for each topic. Tapping the 'Topics' button in the navigation bar navigates us back to the topics table, and we see that all of the topics are functioning correctly, with our custom nav bar names & colors, topic images & titles. Also, in the console you can see the URL being built correctly.

Latest Episodes Tab

Now, we're going create a table view for our "Latest" tab. Like before, we'll create a table with an empty array, and give it a transparent background, so when we scroll past the end of our table view, the background underneath will show through.

...
var latestTable = Titanium.UI.createTableView({data: [], backgroundColor:'transparent'});
var latestXhr = Titanium.Network.createHTTPClient();
latestXhr.onload = function() {
  var latestEpisodes = JSON.parse(this.responseText);
  var data = episodesToData(latestEpisodes);
  latestTable.setData(data);
};
latestWindow.add(latestTable);
latestXhr.open('GET', 'http://screencasts.org/episodes/latest.json');
latestXhr.send();

Then, we'll create a new HTTPClient instance called latestXhr. We'll implement it's onload function, which will parse the JSON into a latestEpisodes array. We'll then build the data array for our latestTable by again calling episodesToData, passing in the latestEpisodes array. We'll then setData for the latestTable to our data array.

Next, we'll add our latestTable to the latestWindow, then tell our HTTPClient to open() a GET request, passing in our latest episodes JSON URL. Then, we'll call send(), to trigger the request.

If we launch our app in Titanium, we'll see that this table view is working correctly on the first tab.

Playing Video

The final step for this app is to play the videos. We're going to add a click event listener so that whenever an episode row is tapped, that screencast video will be launched in a player.

In order to get the actual video URL, we need to make another HTTPClient instance, and send a request to Blip.tv, which is where we host our videos. We'll send a GET request to a URL provided by the Blip.tv API. In the API URL, we'll include the episode's video_blip_id, which we've saved as a variable in each row.

http://blip.tv/players/episode/' + e.row.video_blip_id + '?skin=json&callback=getVideoData&version=2

This will return a JSON feed with a lot of data about our video, but we're just going to pick two URLs out of it. We're going to use the SD video URL, for iPhone 3GS and below, and then the HD video URL for iPhone 4s.

Since we're going to be using the same behavior when 'latest episodes' rows and regular 'episode' rows are tapped, we're going to create a named function that we can reuse for both click event listeners. Let's call it loadRemoteMovie.

...
function loadRemoteMovie(e){
  if(e.row.hasChild){
    loadVideoXhr.open('GET','http://blip.tv/players/episode/'+e.row.video_blip_id+'?skin=json&callback=getVideoData&version=2');
    loadVideoXhr.send();
  };
};
...

In this function, we're first going to check to see if the row's hasChild variable is set to true. This is one of the variables we set on all rows that actually contain a screencast. It would only be absent if the row tapped was one of the extra "empty rows" we added to fill some of our singleTopicTable views. Then we open a loadVideoXhr HTTPClient that we're going to implement in just a minute. We'll open a GET request, passing in the Blip.tv API URL that will give us our episode data. Then, we send the request.

After we've defined the function, we're going to add it to click event listeners for both the singleTopicTable and latestTable.

...
singleTopicTable.addEventListener("click", loadRemoteMovie);
latestTable.addEventListener("click", loadRemoteMovie);

The Blip.tv API URL requires a callback, so we need to setup a callback function with the name we're passing into the GET request. In our case, we're calling it getVideoData.

...
function getVideoData(jsonData){
  return jsonData[0];
};
...

The JSON data that Blip.tv returns is an array with one object. So we're going to return jsonData[0] (jsonData at index zero).

Video HTTPClient()

So let's create the HTTPClient to talk to Blip.tv . We'll have to include this above our loadRemoteMovie function, since that function calls the HTTPClient object we're now creating.

...
var loadVideoXhr = Titanium.Network.createHTTPClient();
loadVideoXhr.onload = function() {
  var blipTVjson = eval(this.responseText);
};
...

Blip.tv doesn't offer a plain-old JSON feed in their API. It does, however, provide JSONP, which is essentially JavaScript code. Therefore, the response text we receive needs to be evaluated with eval as opposed to parsed, as we did with our Screencasts' JSON feeds.

Next we're going to add some code that will use the HD URL, for iPhone 4s and the simulator, and then use the SD URL for iPhone models 3GS and older. To do this, we use Titanium.Platform.model, which we compare against the strings iPhone4 and Simulator.

...
loadVideoXhr.onload = function() {
  ...
  var blipTVURL;
  if(Titanium.Platform.model == 'iPhone 4' || Titanium.Platform.model == 'Simulator') {
    blipTVURL = blipTVjson.additionalMedia[2].url;
  } else {
    blipTVURL = blipTVjson.mediaUrl;
  }
};
...

Next, we create our video player with Titanium.Media.createVideoPlayer, assign its URL, and set fullscreen to true.

...
loadVideoXhr.onload = function() {
  ...
  var videoPlayer = Titanium.Media.createVideoPlayer({
    url:blipTVURL,
    fullscreen:true
  });
};
...

Next, we're going to create a window to hold the videoPlayer called videoWindow. We'll hide the nav bar, and set a custom variable, isClosed, to false. We'll come back to this isClosed variable in a minute. We'll also set orientationModes to an array that includes all 4 possible rotation constants, since we want people be able to rotate the videoWindow as desired.

...
loadVideoXhr.onload = function() {
  ...
  var videoWindow = Titanium.UI.createWindow({
    navBarHidden: true,
    isClosed: false,
    orientationModes: [
    Titanium.UI.LANDSCAPE_RIGHT,
    Titanium.UI.LANDSCAPE_LEFT,
    Titanium.UI.PORTRAIT,
    Titanium.UI.UPSIDE_PORTRAIT    
    ]
  });
};
...

Video 'fullscreen' event listener

The videoPlayer has several default events which you can assign listeners to. We're going to make use of the fullscreen event to simplify the way users exit a video.

...
loadVideoXhr.onload = function() {
  ...
  videoPlayer.addEventListener('fullscreen', function(e){
    if(!e.entering && !videoWindow.isClosed) {
      videoWindow.orientationModes = [Titanium.UI.PORTRAIT];
      videoPlayer.stop();
      videoWindow.close({animated:false});
      videoWindow.isClosed = true;
      videoPlayer.release();
    }
  });
};
...

When a video launches, it will trigger this fullscreen event, since we're setting fullscreen to true. So we also want to check if we're entering the player, which is another default event. If we're just entering, we don't want to close the videoWindow. So if a user has finished watching the video, and clicks the Done button in the top left corner, it will trigger the fullscreen event. e.entering will be false, and isClosed is false as we set a minute ago, so the code inside this if-statement will run.

First, we reset the orientationModes for our videoWindow to only allow portrait-mode. We're doing this to fix some odd behavior we were seeing when leaving this window, so it's kind of a work-around. Next, we stop the videoPlayer, and close the videoWindow. Then, we set our isClosed variable to true, so we don't accidentally run through this loop again if the fullscreen event is triggered again for any reason. Then, we release the videoPlayer to free up resources on the mobile device.

...
loadVideoXhr.onload = function() {
  ...
  videoWindow.open({modal:true, animated:false});
  videoWindow.add(videoPlayer);
  videoPlayer.show();
  videoPlayer.play();
};
...

Finally, outside of the fullscreen event listener, but still in our onload function, we'll run through the final steps to actually open a video. We open our videoWindow as a modal window by setting modal to true. We're also setting animated to false to stop the modal window from animating up from bottom of the screen. If you're unfamiliar with the term "modal window", it just means that we're opening it up "in front" of our other windows. This type of window is intended for things that temporarily interrupt the flow of the application, then let the user do or experience something, and finally return to the app's normal flow when closed. Then we add the videoPlayer to our videoWindow, show it, and finally play it.

If we launch the app again in Titanium, we'll see that our videos are loading correctly when a row is tapped. And if we rotate the simulator, by pressing Command + the left or right arrow, we see that the video window rotates correctly. And finally, if we click the done button, our video window removes itself properly.

And that's it!

We're going to submit this app to the App Store, so be sure to watch for it there.

Thanks for watching! Subscribe to our RSS feed, follow us on Twitter, and please leave any questions, comments or suggestions for new screencasts in the comments below. If you like our videos, and think your friends, followers or colleagues would benefit from seeing them, please feel free share via any of the links below the video. We really appreciate your support.

See you next time!

Update - 30 May 2011

Blip.tv have changed their API so we've pushed an update to the App Store.

To fix the code yourself, all you need to do is change:

...
function getVideoData(jsonData){
  return jsonData[0];
};
...

To:

...
function getVideoData(jsonData){
  return jsonData[0][0];
};
...

Update - 21 June 2011

Blip.tv have changed their API again. To fix use the following code:

...
function getVideoData(jsonData){
  return jsonData[0].Post;
};
...

← Latest Episodes

blog comments powered by Disqus