If you know HTML, CSS, and JavaScript, you already have what you need to develop your own iPhone apps. With this book, you'll learn how to use these open source web technologies to design and build apps for both the iPhone and iPod Touch. Buy the print book or ebook or purchase the iPhone App. |
In our quest to build an iPhone app without Objective-C, we’ve so far learned how to use CSS to style a collection of HTML pages to look like an iPhone app. In this chapter, we’ll lay the groundwork to make those same pages behave like an iPhone app. Specifically, we’ll discuss using Ajax to turn a full website into a single-page app, how to create a back button with history using JavaScript, and how to take advantage of the Web Clip icon and full screen mode features of the iPhone to launch your app without Mobile Safari intruding upon the user experience.
The term Ajax has become such a buzzword that I’m not even sure I know what it means anymore. For the purposes of this book, I’m going to use Ajax to refer to the technique of using JavaScript to send requests to a web server without reloading the current page (e.g., to retrieve some HTML, submit a form, and so on). This approach makes for a very smooth user experience, but does require that you reinvent a lot of wheels.
For example, if you are loading external pages dynamically, the browser will not give any indication of progress or errors to the users. Furthermore, the back button will not work as expected unless you take pains to support it. In other words, you have to do a lot of work to make a sweet Ajax app. Even so, there are some very good reasons to go to the trouble. In particular, it opens the door to creating iPhone apps that can run full-screen (the section called “Full Screen Mode”) and even offline (Chapter 6, Going Offline).
For my next series of examples, I’m going to write a single
page called iphone.html
that will sit in front of all
of the site’s other pages and will handle requests, sort of like a traffic
cop. Here’s how it works. On first load, iphone.html
will present the user with a nicely formatted version of the site
navigation. I’ll then use jQuery to “hijack” the onclick
actions of the nav
links so that when the user clicks on one,
the browser page will not navigate to the target
link. Rather, jQuery will load a portion of the HTML from the remote page
and deliver the data to the user by updating the current page. I’ll start
with the most basic functional version of the code and improve it as we go
along.
The HTML for the
iphone.html
wrapper page is extremely simple (see
Example 3.1, “This simple HTML wrapper markup will sit in front of all the
site’s other pages”). In the head
section, I set
the title
and viewport
options, and include
links to a stylesheet (iphone.css
) and two JavaScript
files: jquery.js
and a custom JavaScript file named
iphone.js
.
For more information on where to get
jquery.js
and what to do with it, see the section called “Intro to JavaScript”.
The body just has two div
containers: a header with the initial title in an h1
tag, and
an empty div
container, which will end up holding HTML
snippets retrieved from other pages.
Example 3.1. This simple HTML wrapper markup will sit in front of all the site’s other pages
<html> <head> <title>Jonathan Stark</title> <meta name="viewport" content="user-scalable=no, width=device-width" /> <link rel="stylesheet" href="iphone.css" type="text/css" media="screen" /> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="iphone.js"></script> </head> <body> <div id="header"><h1>Jonathan Stark</h1></div> <div id="container"></div> </body> </html>
Moving on to the
iphone.css
file, you can see in Example 3.2, “The base CSS for the page is just a slightly reshuffled version
of previous examples” that I’ve reshuffled some of the properties from
previous examples (e.g., some of the #header h1
properties
have been moved up to #header
). Overall, though, everything
should look familiar (if not, please review ).
Example 3.2. The base CSS for the page is just a slightly reshuffled version of previous examples
body { background-color: #ddd; color: #222; font-family: Helvetica; font-size: 14px; margin: 0; padding: 0; } #header { background-color: #ccc; background-image: -webkit-gradient(linear, left top, left bottom, from(#ccc), to(#999)); border-color: #666; border-style: solid; border-width: 0 0 1px 0; } #header h1 { color: #222; font-size: 20px; font-weight: bold; margin: 0 auto; padding: 10px 0; text-align: center; text-shadow: 0px 1px 0px #fff; } ul { list-style: none; margin: 10px; padding: 0; } ul li a { background-color: #FFF; border: 1px solid #999; color: #222; display: block; font-size: 17px; font-weight: bold; margin-bottom: -1px; padding: 12px 10px; text-decoration: none; } ul li:first-child a { -webkit-border-top-left-radius: 8px; -webkit-border-top-right-radius: 8px; } ul li:last-child a { -webkit-border-bottom-left-radius: 8px; -webkit-border-bottom-right-radius: 8px; } ul li a:active,ul li a:hover { background-color:blue; color:white; } #content { padding: 10px; text-shadow: 0px 1px 0px #fff; } #content a { color: blue; }
The JavaScript in
iphone.js
is where all the magic happens in this
example. Please refer to Example 3.3, “This bit of JavaScript in iphone.js converts the links on the
page to Ajax requests” as I go
through it line by line.
This JavaScript loads a document called
index.html
, and will not work without it. You
should reuse the HTML file from Chapter 2, Basic iPhone Styling,
being sure to save it as index.html
in the same
directory as the iphone.html
you created earlier in
this chapter. However, none of the links in it will work unless the
targets of the links actually exist. You can create these files yourself
or download the example code from the book’s website.
Creating about.html
,
blog.html
, and
consulting-clinic.html
will give you a few links to
play with. To do so, just duplicate index.html
a
few times and change the filename of each copy to match the related
link. For added effect, you can change the content of the
h2
tag in each file to match the filename. For example, the
h2
in blog.html
would be
<h2>Blog</h2>
.
Example 3.3. This bit of JavaScript in iphone.js converts the links on the page to Ajax requests
$(document).ready(function(){ loadPage(); }); function loadPage(url) { if (url == undefined) { $('#container').load('index.html #header ul', hijackLinks); } else { $('#container').load(url + ' #content', hijackLinks); } } function hijackLinks() { $('#container a').click(function(e){ e.preventDefault(); loadPage(e.target.href); }); }
Here I’m using jQuery’s document ready
function to have the browser run the | |
The | |
If a value is not sent into the function,
| |
This line is executed if the
| |
Once the | |
On this line, | |
Normally, a web browser will navigate to a
new page when a link is clicked. This navigation response is called
the “default behavior” of the link. Since we are handling clicks and
loading pages manually, we need to prevent this default behavior. On
this line, I’ve done so by calling the built-in
| |
When the user clicks, I pass the URL of the
remote page to the |
One of my favorite things about JavaScript is that you can pass a function as a parameter to another function. Although this looks weird at first, it’s extremely powerful and allows you to make your code modular and reusable. If you’d like to learn more, you should check out JavaScript: The Good Parts by Douglas Crockford (O’Reilly). In fact, if you are working with JavaScript, you should check out everything by Douglas Crockford; you’ll be glad you did.
Click handlers do not run when the page first loads; they run when the user has read some stuff on the page and decides to click a link. Assigning click handlers is like setting booby traps; you do some initial setup work for something that may or may not be triggered later.
It’s worth taking a few minutes to read up on the properties of the event object that JavaScript creates in response to user actions in the browser. A good reference is located at http://www.w3schools.com/htmldom/dom_obj_event.asp.
With this tiny bit of HTML, CSS, and JavaScript, we have essentially turned an entire website into a single-page application. However, it still leaves quite a bit to be desired. Let’s slick things up a bit.
Since we are not allowing the browser to navigate from page to page, the user will not see any indication of progress while data is loading. We need to provide some feedback to let users know that something is, in fact, happening. Without this feedback, users will wonder if they actually clicked the link or missed it, and will often start clicking all over the place in frustration. This can lead to increased server load and application instability (i.e., crashing).
If you are testing this web application on a local network, the network
speeds will be so fast you won’t ever see the progress indicator. If you
are using Mac OS X, you can slow all incoming web traffic by typing a
couple of ipfw
commands at the terminal. For example,
these commands will slow all web traffic to 4 kilobytes per
second:
sudo ipfw pipe 1 config bw 4KByte/s sudo ipfw add 100 pipe 1 tcp from any to me 80
If you are using the Safari desktop browser to view the pages, you’ll need to use your
Mac’s hostname or external IP address in the URL (for example,
mymac.local
rather than localhost
). When
you’re done testing, delete the rule with sudo ipfw delete
100
(you can delete all custom rules with ipfw
flush
).
Thanks to jQuery, providing this sort of feedback only takes two lines of
code. We’ll just append a loading div
to the body when
loadPage()
starts, and remove the loading div
when hijackLinks()
is done. Example 3.4, “Adding a simple progress indicator to the page” shows a modified version of Example 3.3, “This bit of JavaScript in iphone.js converts the links on the
page to Ajax requests”. The lines you need to add to
iphone.js
are shown in bold.
Example 3.4. Adding a simple progress indicator to the page
$(document).ready(function(){ loadPage(); }); function loadPage(url) { $('body').append('<div id="progress">Loading...</div>'); if (url == undefined) { $('#container').load('index.html #header ul', hijackLinks); } else { $('#container').load(url + ' #content', hijackLinks); } } function hijackLinks() { $('#container a').click(function(e){ e.preventDefault(); loadPage(e.target.href); }); $('#progress').remove(); }
See Example 3.5, “CSS added to iphone.css used to style the progress
indicator” for the CSS that needs to be
added to iphone.css
to style the progress
div
. The result can be seen in Figure 3.1, “Without a progress indicator of some kind, your app will seem
unresponsive and your users will get frustrated”.
Figure 3.1. Without a progress indicator of some kind, your app will seem unresponsive and your users will get frustrated
Example 3.5. CSS added to iphone.css used to style the progress indicator
#progress { -webkit-border-radius: 10px; background-color: rgba(0,0,0,.7); color: white; font-size: 18px; font-weight: bold; height: 80px; left: 60px; line-height: 80px; margin: 0 auto; position: absolute; text-align: center; top: 120px; width: 200px; }
My site happens to have a single
h2
at the beginning of each page that would make a nice page
title (see Figure 3.2, “Before moving the page heading to the toolbar...”). You can see
this in the HTML source shown in Chapter 2, Basic iPhone Styling. To be
more iPhone-esque, I’m going to pull that title out of the content and put
it in the header (see Figure 3.3, “...and after moving the page heading to the toolbar”).
Again, jQuery to the rescue: you can just add three lines to the
hijackLinks()
function to make it happen. Example 3.6, “Using the h2 from the target page as the toolbar title” shows the hijackLinks
function with these
changes.
Example 3.6. Using the h2 from the target page as the toolbar title
function hijackLinks() { $('#container a').click(function(e){ e.preventDefault(); loadPage(e.target.href); }); var title = $('h2').html() || 'Hello!'; $('h1').html(title); $('h2').remove(); $('#progress').remove(); }
Note that I added the title lines before the line that removes the progress indicator. I like to remove the progress indicator as the very last action because I think it makes the application feel more responsive.
The double pipe (||
) in the first
line of inserted code (shown in bold) is the JavaScript logical operator
OR. Translated into English, that line would read: “Set the title variable
to the HTML contents of the h2
element, or to the string
‘Hello!’ if there is no h2
element.” This is important
because the first page load won’t contain an h2
, as we are
just grabbing the nav ul
s.
This point probably needs some clarification.
When users first load the iphone.html
URL, they are
only going to see the overall site navigation elements, as opposed to
any site content. They won’t see any site content until they tap a link
on this initial navigation page.
A few pages on my site have titles that are
longer than can fit in the header bar (Figure 3.4, “Text wrapping in the toolbar is not very iPhone-ish...”).
I could just let the text break onto more than one line, but that would
not be very iPhone-ish. Rather, I’ve updated the #header h1
styles such that long text will be truncated with a trailing ellipsis (see
Figure 3.5, “...but we can beautify it with a CSS ellipsis” and Example 3.7, “Adding an ellipsis to text that is too long for its
container”).
This might be my favorite little-known CSS trick.
Example 3.7. Adding an ellipsis to text that is too long for its container
#header h1 { color: #222; font-size: 20px; font-weight: bold; margin: 0 auto; padding: 10px 0; text-align: center; text-shadow: 0px 1px 0px #fff; max-width: 160px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
Here’s the rundown:
max-width: 160px
instructs the browser not to allow the
h1
element to grow wider than 160px. Then, overflow:
hidden
instructs the browser to chop off any content that extends
outside of the element borders. Next, white-space: nowrap
prevents the browser from breaking the line into two. Without this line,
the h1
would just get taller to accommodate the text at the
defined width. Finally, text-overflow: ellipsis
appends three
dots to the end of any chopped-off text to indicate to users that they are
not seeing the entire string.
Let’s say you have an About page that is longer than the viewable area on the iPhone. The user visits the page, scrolls down to the bottom, and clicks on a link to your Contact page. If you have more than a screenful of text on your Contact page, the new data will appear with the window still scrolled all the way to the bottom.
Technically, this makes sense because we are
not actually leaving the current (scrolled) page, but it’s certainly
confusing for the user. To rectify the situation, I have added a
scrollTo()
command to the loadPage()
function
(see Example 3.8, “It’s a good idea to scroll back to the top when a user navigates
to a new page”).
Now whenever a user clicks a link, the page will first jump to the top. This has the added benefit of ensuring that the loading graphic is visible if the user clicks a link at the bottom of a long page.
Example 3.8. It’s a good idea to scroll back to the top when a user navigates to a new page
function loadPage(url) {
$('body').append('<div id="progress">Loading...</div>');
scrollTo(0,0);
if (url == undefined) {
$('#container').load('index.html #header ul', hijackLinks);
} else {
$('#container').load(url + ' #content', hijackLinks);
}
}
Like most sites, mine has links to external pages (i.e., pages hosted on other domains). I don’t want to hijack these external links because it wouldn’t make sense to inject their HTML into my iPhone-specific layout. In Example 3.9, “You can allow external pages to load normally by checking the domain name of the URL”, I have added a conditional that checks the URL for the existence of my domain name. If it’s found, the link is hijacked and the content is loaded into the current page; that is, Ajax is in effect. If not, the browser will navigate to the URL normally.
You must change
jonathanstark.com
to the appropriate domain or hostname for
your website, or the links to pages on your website will no longer be
hijacked.
Example 3.9. You can allow external pages to load normally by checking the domain name of the URL
function hijackLinks() { $('#container a').click(function(e){ var url = e.target.href; if (url.match(/jonathanstark.com/)) { e.preventDefault(); loadPage(url); } }); var title = $('h2').html() || 'Hello!'; $('h1').html(title); $('h2').remove(); $('#progress').remove(); }
The url.match
function uses a
language, regular expressions, that is often embedded within other
programming languages such as JavaScript, PHP, and Perl. Although this
regular expression is simple, more complex expressions can be a bit
intimidating, but are well worth becoming familiar with. My favorite
regex page is located at http://www.regular-expressions.info/javascriptexample.html.
The elephant in the room at this point is that the user has no way to navigate back to previous pages (remember that we’ve hijacked all the links, so the Safari page history won’t work). Let’s address that by adding a back button to the top-left corner of the screen. First, I’ll update the JavaScript, and then I’ll do the CSS.
Adding a standard iPhone-ized back button to the app means keeping track of the user’s click history. To do this, we’ll have to A) store the URL of the previous page so we know where to go back to, and B) store the title of the previous page so we know what label to put on the back button.
Adding this feature touches on most of the
JavaScript we’ve written so far in this chapter, so I’ll go over the
entire new version of iphone.js
line by line (see
Example 3.10, “Expanding the existing JavaScript example to include support for
a back button”). The result will look
like Figure 3.6, “It wouldn’t be an iPhone app without a glossy, left-arrow back
button”.
Example 3.10. Expanding the existing JavaScript example to include support for a back button
var hist = []; var startUrl = 'index.html'; $(document).ready(function(){ loadPage(startUrl); }); function loadPage(url) { $('body').append('<div id="progress">Loading...</div>'); scrollTo(0,0); if (url == startUrl) { var element = ' #header ul'; } else { var element = ' #content'; } $('#container').load(url + element, function(){ var title = $('h2').html() || 'Hello!'; $('h1').html(title); $('h2').remove(); $('.leftButton').remove(); hist.unshift({'url':url, 'title':title}); if (hist.length > 1) { $('#header').append('<div class="leftButton">'+hist[1].title+'</div>'); $('#header .leftButton').click(function(){ var thisPage = hist.shift(); var previousPage = hist.shift(); loadPage(previousPage.url); }); } $('#container a').click(function(e){ var url = e.target.href; if (url.match(/jonathanstark.com/)) { e.preventDefault(); loadPage(url); } }); $('#progress').remove(); }); }
On this line, I’m initializing a variable
named | |
Here I’m defining the relative URL of the
remote page to load when the user first visits
| |
This line and the next make up the document
ready function definition. Note that unlike previous examples, I’m
passing the start page to the | |
On to the | |
This | |
On this line, the URL parameter and the
appropriate source element are concatenated as the first parameter
passed to the load function. As for the second parameter, I’m passing
an anonymous function (an unnamed function that
is defined inline) directly. As we go through the anonymous function,
you’ll notice a strong resemblance to the | |
On this line, I’m removing the
| |
Here I’m using the built-in
| |
On this line, I’m using the built-in
| |
Next, I’m adding that
| |
In this block of code, I’m binding an anonymous function to the click handler of the back button. Remember, click handler code executes when the user clicks, not when the page loads. So, after the page loads and the user clicks to go back, the code inside this function will run. | |
This line and the next use the built-in
| |
The remaining lines were copied exactly from previous examples, so I won’t rehash them here. | |
This is the URL matching code introduced
earlier in this chapter. Remember to replace
|
Please visit http://www.hunlock.com/blogs/Mastering_Javascript_Arrays for a full listing of JavaScript array functions with descriptions and examples.
Now that we have our back button, all that
remains is to purty it up with some CSS (see Example 3.11, “Add the following to iphone.css to beautify the back button with
a border image”). I start off by styling the text with
font-weight
, text-align
,
line-height
, color
, and
text-shadow
. I continue by placing the div
precisely where I want it on the page with position
,
top
, and left
. Then, I make sure that long text
on the button label will truncate with an ellipsis using
max-width
, white-space
, overflow
,
and text-overflow
. Finally, I apply a graphic with
border-width
and -webkit-border-image
. Unlike my
earlier border image example, this image has a different width for the
left and right borders, because the image is made asymmetrical by the
arrowhead on the left side.
Don’t forget that you’ll need an image
for this button. You’ll need to save it as
back_button.png
in the images
folder underneath the folder that holds your HTML file. See the section called “Adding Basic Behavior with jQuery” for tips on finding or creating your
own button images.
Example 3.11. Add the following to iphone.css to beautify the back button with a border image
#header div.leftButton { font-weight: bold; text-align: center; line-height: 28px; color: white; text-shadow: rgba(0,0,0,0.6) 0px -1px 0px; position: absolute; top: 7px; left: 6px; max-width: 50px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border-width: 0 8px 0 14px; -webkit-border-image: url(images/back_button.png) 0 8 0 14; }
By default, Mobile Safari briefly displays a translucent gray box over clickable
objects that have been tapped (Figure 3.7, “By default, Mobile Safari displays a translucent gray box over
clickable objects that have been tapped”).
Since our back button is not rectangular, this effect looks a little lame,
but removing it is easy and makes the app look much better. Mobile Safari
supports a property called -webkit-tap-highlight-color
that
allows you to change the default to whatever color you like. I want to
remove the highlight completely, which I’ve done here by setting the tap
highlight to a fully transparent color (see Example 3.12, “Add the following to iphone.css to remove the default tap
highlight from Mobile Safari”).
Figure 3.7. By default, Mobile Safari displays a translucent gray box over clickable objects that have been tapped
Example 3.12. Add the following to iphone.css to remove the default tap highlight from Mobile Safari
#header div.leftButton {
font-weight: bold;
text-align: center;
line-height: 28px;
color: white;
text-shadow: rgba(0,0,0,0.6) 0px -1px 0px;
position: absolute;
top: 7px;
left: 6px;
max-width: 50px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
border-width: 0 8px 0 14px;
-webkit-border-image: url(images/back_button.png) 0 8 0 14;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
In the case of the back button, there can be at
least a second or two of delay before the content from the previous page
appears. To avoid frustration, I want the button to look clicked the
instant it’s tapped. In a desktop browser, this would be a simple process;
you’d just add a declaration to your CSS using the
:active
pseudoclass to specify an alternate style for
the object that was clicked. I don’t know whether it’s a bug or a feature,
but this approach does not work on the iPhone; the
:active
style is ignored.
I toyed around with combinations of
:active
and :hover
, which brought me
some success with non-Ajax apps. However, with an Ajax app like the one we
are using here, the :hover
style is sticky (i.e., the
button appears to remain “clicked” even after the finger is
removed).
Fortunately, the fix is pretty simple. I use
jQuery to add the class clicked
to the button when the user
taps it. I’ve opted to apply a darker version of the button image to the
button in the example (see Figure 3.8, “It’s a subtle difference, but the clicked back button is a bit
darker than the default state”
and Example 3.13, “Add the following to iphone.css to make the back button look
clicked the moment the user taps it”). You’ll need to
make sure you have a button image called
back_button_clicked.png
in the
images
subfolder. See the section called “Adding Basic Behavior with jQuery” for tips on finding or creating your own
button images.
Figure 3.8. It’s a subtle difference, but the clicked back button is a bit darker than the default state
Example 3.13. Add the following to iphone.css to make the back button look clicked the moment the user taps it
#header div.leftButton.clicked { -webkit-border-image: url(images/back_button_clicked.png) 0 8 0 14; }
Since I’m using an image for the clicked style, it would be smart to preload the image. Otherwise, the unclicked button graphic will disappear the first time it’s tapped while the clicked graphic downloads. I’ll cover image preloading in the next chapter.
With the CSS in place, I can now update the
portion of iphone.js
that assigns the click handler
to the back button. First, I add a variable, e
, to the
anonymous function in order to capture the incoming click event. Then, I
wrap the event target in a jQuery selector and call jQuery’s
addClass()
function to assign my clicked CSS class to the
button:
$('#header .leftButton').click(function(e){ $(e.target).addClass('clicked'); var thisPage = hist.shift(); var previousPage = hist.shift(); loadPage(previousPage.url); });
Hopefully users will want to add an icon for your web app (called a “Web Clip icon”) to their home screens. They do this by tapping the plus button at the bottom of the Safari window (Figure 3.9, “Adding a Web Clip icon to your home screen, Step 1: click the plus button at the bottom of the Safari window”), tapping Add to Home Screen (Figure 3.10, “Step 2: click the “Add to Home Screen” button in the dialog”), and clicking the Add button (Figure 3.11, “Step 3: click the “Add” button in the “Add to Home” panel”). By default, the iPhone will create this icon by thumbnailing the current page (including position and zoom) and applying rounded corners and a glossy effect (Figure 3.12, “Step 4: a 57 × 57 pixel image will show up on the home screen”).
Figure 3.9. Adding a Web Clip icon to your home screen, Step 1: click the plus button at the bottom of the Safari window
To customize the home
screen image, the cool kids provide a custom Web Clip icon. The simplest
way to do this is to specify a single icon for your entire site by
uploading a file named apple-touch-icon.png
to your web root. The file should be 57 pixels square, and
without gloss or rounded corners because the iPhone will add these
automatically. If you don’t want the iPhone to add effects to your Web
Clip icon, change the name of the file to
apple-touch-icon-precomposed.png
.
In some cases, you may want to provide a Web
Clip icon for a page that is different from the rest of your site. You can
do this by adding one of the following lines to the head
section of the “traffic cop” HTML document,
iphone.html
(replacing myCustomIcon.png
with the absolute or relative
path to the image):
<link rel="apple-touch-icon" href="myCustomIcon.png" /> <link rel="apple-touch-icon-precomposed" href="myCustomIcon.png" />
Feel like reclaiming a quarter of the available vertical
space from Mobile Safari (104 pixels, to be precise)? Add the following line to the
head
section of the “traffic cop” HTML document,
iphone.html
, and your web app will display in full
screen mode when launched from the Web Clip icon:
<meta name="apple-mobile-web-app-capable" content="yes" />
I would’ve told you about this feature earlier, but it’s only useful once you have hijacked all of your hyperlinks with Ajax. As soon as a user clicks on a nonhijacked link—one that actually navigates to a new page—Mobile Safari will launch and load the page normally. This behavior is perfect for the example we’ve been working with because external links (Amazon, Twitter, Facebook, etc.) will open in Safari.
Once you’ve added the
apple-mobile-web-app-capable
meta tag, you have the option
to control the background color of the 20-pixel status bar at the top of
the screen using the apple-mobile-web-app-status-bar-style
meta tag. The normal gray Safari status bar is the default, or you
can change it to black
(see Figure 3.13, “Full screen mode gives you about 25% more screen real estate
and allows you to customize the appearance of the status bar”). You can also set it to
black-translucent
, which makes it partially transparent and
additionally removes it from the document flow. In other words, your
content will be shifted up by 20 pixels and behind the status bar when
the page first loads, so you might have to position your header a little
lower to compensate:
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
Changes to the status bar style will only take effect when the app is launched in full screen mode.
When an app is launched in full screen mode, the user is presented with a screenshot of the app while the first page is loading. I’m not a fan of this because it looks like the app is ready to be interacted with, when in reality tapping a link will do nothing. Furthermore, the screenshot is based on the last page from the user’s previous visit, scrolled to wherever he left off—not very attractive.
Fortunately, Mobile Safari allows us to
define a startup graphic that will be displayed while the page is
loading. To add a custom startup graphic, create a 320px × 460px PNG
file and place it in the same directory with
iphone.html
. Next, add the following line to the
head
section of iphone.html
(you’d
replace myCustomStartupGraphic.png
with the absolute or
relative path to your image):
<link rel="apple-touch-startup-image" href="myCustomStartupGraphic.png" />
The next time we launch our app from the Web Clip icon, the default loading behavior will take place while the new custom graphic is downloaded. On the subsequent launch, the custom startup graphic will be displayed (Figure 3.14, “Providing a custom startup graphic for an app launched in full screen mode”).
In this chapter, you’ve learned how to convert a normal website into a full-screen Ajax application, complete with progress indicators, a native-looking back button, and a custom Web Clip icon. In the next chapter, you’ll learn how to make your app come alive by adding native user interface animations. That’s right; here comes the fun stuff!