Building iPhone Apps with JavaScript using Titanium
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's
dataoption. Each entry in the tableview's
data` 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.additionalMedia2.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;
};
...