Sunday, February 24, 2008 @ 6:30 PM
Part 9: Gratuitous Eye-Candy

Games just aren't interesting without snazzy graphics. But graphics need to be downloaded, and you want to limit as many unnecessary downloads as possible when developing applications for the iPhone.
Until now, our game required only two images. The background image, and the image border used for grid highlights. Together these assets weigh in at about 24.5K. We also embedded a number of images in Javascript, which beefed up that file. All together the images, HTML file (which includes CSS) and Javascript file amount to 50K. That's really good for a complete game and means that the game will load quickly if you need to access it using EDGE.
But of all the things that irk me in life, only one is intolerable. A shitty ending to a video game. I mean you spend 40+ hours of your life (many of which are extremely aggravating) glued to the game. Is it too much to ask for a slick 5+ minute movie ending? Something brilliant that wraps up everything at the end. It only seems appropriate. So, I think it's important to give the winner of our puzzle something nice to look at when they're done.
End Screen:
Okay, it's not much, but hey, this game is being developed for demonstration purposes. Still, I think it satisfies the "snazzy graphics" requirement. Keep in mind that by the time you see the curl, the grid will be filled with letters. I didn't want to give away all of the words so I left the grid empty.In my "Preloading Commonly Used Game Assets" post I mentioned that it would not make sense to embed all images in Javascript using URI Kitchen. The curl PNG weighs 69K. That's more than the entire game weighed up until this point. So, is it worth forcing the user to wait for this image to download when they won't even see it until the end of the game? Sure it is. Because we're not going to "force them to wait."
Since this image isn't required until the very end of the game (and it would probably take anyone at least 15 mins to solve the puzzle), we're going to load the image behind the scenes while the user is playing the game. Here's how we'll do it:
First, we set the game up. This includes waiting for all of the assets (except the curl) to download. Once the HTML has been parsed, Javascript hides the iPhone address bar, reads the game's XML file and builds the game's grid. The game is now ready to be played.
At this point we execute the following Javascript:
setTimeout(function() {
var curl = new Image();
curl.src = 'curl.png';
curl.onload = function() {
var img = document.createElement('img');
img.id = 'curl';
img.src = curl.src;
document.getElementById('body').appendChild(img);
img.onclick = function() { resetBoard(across, down); };
};
}, 30000);Again, the above script will not execute until the game has completely downloaded and is playable by the user. This script then waits 30 seconds before it downloads and sets up the curl image, giving the game more than enough time to prepare the asset before it's required.
For dramatic effect, we're going to fade the curl in when the user has completed the puzzle. There are a number of Javascript resources you can utilize to create fades. MooTools is a great option. But since we're not relying on MooTools to manage the styles or effects of any of the other elements that make up the game, it doesn't make sense to include the MooTools Javascript library (an extra download) only to access it for one effect. There's a simpler way.
The Mac software company Panic has, for a long time, been a leader in creating compelling, and shockingly easy to use, Web applications (they of course make brilliant Mac applications as well). On their site I found a small snippet of math which we can use to fade the curl.
Javascript:
function cubicOut(t, b, c, d) { return c*((t = t/d-1)*t*t+1)+b; }Using the cubicOut function, we can do the following:
var curlanim = { time:0, begin:0, change:0.0, duration:0.0, timer:null };
function curlFade(start, end, duration) {
if (end == 1) document.getElementById('curl').style.visibility = 'visible';
if (curlanim.timer != null) { clearInterval(curlanim.timer); curlanim.timer = null; }
curlanim.time = 0;
curlanim.begin = start;
curlanim.change = end-start;
curlanim.duration = duration;
curlanim.timer = setInterval(function() { fadeAnimDo(end) }, 15);
}
function fadeAnimDo(e) {
if (curlanim.time > curlanim.duration) {
clearInterval(curlanim.timer);
curlanim.timer = null;
if (e == 0) document.getElementById('curl').style.visibility = 'hidden';
} else {
var alpha = cubicOut(curlanim.time, curlanim.begin, curlanim.change, curlanim.duration);
document.getElementById('curl').style.opacity = alpha;
curlanim.time++;
}
}In the last post I mentioned that the matrix array keeps track of whether or not correct letters have been added to the 225 different spaces that make up the puzzle. If it is determined that the puzzle is filled with correct letters (using the matrix array), then the game has been won and we initiate the curl fade with the following code: curlFade(0, 1, 50).
And that's it! The tutorial portion of this feature is complete. Although we didn't post every bit of the game's Javascript, I hope we've explained enough to provide a good sense as to how everything was developed. Come back tomorrow for a link to the working game!
// Ryan Jennings
Labels: crossword, game, how to, iphone development, javascript
MindComet at 6:30 PM - View Post
|
0 comments
Friday, February 22, 2008 @ 5:24 PM
Part 8: Maximizing Screen Real Estate

In the previous post I included a new image of the game. Let me explain how the user is going to interact with the game grid.
Pressing on any square will active a selection (see image below) focusing on whatever horizontal (across) word includes that square. The actual square pressed will turn blue. Pressing anywhere on the now visible selection will rotate it 90° (using the blue square as a control point) to focus on the vertical (down) word. Pressing the selection a second time will deactivate it. You can also deactivate the selection at any time by pressing outside of the selection.
A clue will only be visible underneath the puzzle while a selection is activated. It will initially read something like this:
Press here to answer.
I decided early on that I didn't want to continually display an input field for answering. Rather, I only wanted to display it when the user was actually ready to answer.
Since iPhone applications are dynamic, we can make smart use of the screen real estate and only display certain UI elements when necessary. Here's how I implemented the "now-you-see-it-now-you-don't" input field:
HTML:
<span id="outer-answer"><input type="text" id="answer" value="" /><span style="text-decoration:underline">Press here to answer.</span></span>
CSS:
#outer-answer { position:relative; }
#answer {
width: 208px;
height: 20px;
border: none;
padding: 0;
margin: 0;
position: absolute;
opacity: 0;
text-align: center;
line-height: 20px;
margin-left: 56px;
font: bold 13px Georgia,serif;
}Javascript:
document.getElementById('answer').onfocus = function() {
this.style.opacity = 1;
};
document.getElementById('answer').onblur = function() {
if (this.value != '') {
var letters = this.value.split('');
var correct = xmlhttp.responseXML.getElementsByTagName(node)[0].getAttribute('a').split('');
var numCorrect = 0;
for (var i = 0; i < selected.length; i++) {
if (letters[i]) {
if (letters[i].toUpperCase() == correct[i]) {
eval('ctx.drawImage(i'+letters[i].toLowerCase()+'b, '+((selected[i].id%15)*18)+', '+(Math.floor(selected[i].id/15)*18)+')');
matrix[selected[i].id] = 1;
numCorrect++;
} else {
matrix[selected[i].id] = 0;
}
}
}
if (numCorrect == correct.length) {
alert('Correct!');
clearSelection();
} else if (numCorrect == 0) {
alert('Incorrect, try again.');
document.getElementById('answer').value = '';
document.getElementById('answer').focus();
} else if (numCorrect != correct.length) {
alert('Only some letters are correct.');
clearSelection();
}
checkForWin();
}
};The input field is actually always present (positioned on top of the "press here" text), but it starts off invisible. When you press to answer the field becomes active and the iPhone displays its keyboard. Press the keyboard "Done" button or anywhere on the grid to commit your answer.
So, that's how it's done. Before I wrap up this post, I'll explain some of the other things that are happening in the Javascript above.
Once the user clicks the iPhone's "Done" button we first check to make sure they have actually entered text. If they have, we create an array of the text and match it against an array of the letters that make up the correct answer to the clue. We check each letter one by one. If the letters match, we add the letter to the game grid. The matrix array keeps track of whether or not correct letters have been added to the 225 different spaces that make up the puzzle. Ultimately if all letters are correct, you've won the game.
// Ryan Jennings
Labels: crossword, game, how to, iphone development, javascript
MindComet at 5:24 PM - View Post
|
0 comments
Thursday, February 21, 2008 @ 2:53 AM
Part 7: Taking Advantage of Web Kit CSS Styles

One of the biggest benefits of designing Web applications specifically for the iPhone is that you have access to many Web Kit CSS properties that have not yet been supported in today's other popular browsers, including Firefox.
Things like:
- Borders with rounded corners
- Image borders
- Box shadows
- Multiple background images
The buttons used in iUI all rely on image borders. Image borders make it easy to create scalable image-based buttons (with rounded corners and all types of other snazzy eye candy). Accomplishing something like this previously required 4+ nested elements (plus 4+ images) each responsible for the rendering a specific corner of the button. Now you need only one element and one image and two CSS attributes.
Here's the CSS:
.button {
border-width: 0 5px;
-webkit-border-image: url(toolButton.png) 0 5 0 5;
}
| When using -webkit-border-image you first specify the image to use, then set the areas that will act as your borders. In the example above, we're only going to scale the asset horizontally, so we'll only need a border of 5 pixels on the left and right. The top and bottom do not require a border. | Click Me |
For more information on image borders check out Apple's Safari CSS Reference: Supported CSS Properties.
In the crossword puzzle game we'll use an image border to highlight the currently selected squares in the puzzle grid. Here's what it will look like:
Using an image border is convienent because we can scale the border horizontally and vertically based on the length of the word we're working with. After entering my answer, in this case "CAL," the letters would appear in the highlighted area on the grid.// Ryan Jennings
Labels: crossword, css, game, how to, iphone development, iui, javascript, web kit
MindComet at 2:53 AM - View Post
|
0 comments
Tuesday, February 19, 2008 @ 8:09 PM
Part 6: Taming XML

Though it's easy to get Javascript to read from an XML file, pulling out the appropriate data can sometimes be tricky. Here are some tips to help you tame the XML beast.
Refer to the code we posted yesterday. If the XMLHttpRequest loads without error, then we can use the xmlhttp variable to access the contents of the loaded XML file. To understand how to work with xmlhttp (or whatever you named the XMLHttpRequest instance), relate it to Javascript's document variable.
document.getElementsByName('menu') will return an array of the elements on your page that all share the name "menu."
xmlhttp.responseXML.getElementsByTagName('menu') will return an array of the "menu" XML nodes (tags).
Now, nodes can have both attributes and values. In the following example, "Burns or Byron" is the c attribute of the a1 node and "Timothy Parker" is the value of the editor node.
XML Example:
<?xml version="1.0" encoding="UTF-8"?>
<editor>Timothy Parker</editor>
<across>
<a1 a="POET" c="Burns or Byron" n="1" cn="1" />
</across>
getElementsByTagName will always return an array. In the example above there is only one a1 node, therefore there's no reason to keep an array of a1 nodes. Using the code below, we can save to variable only the first returned node (since that's all that exists).
var a1 = xmlhttp.responseXML.getElementsByTagName('a1')[0];Now, access the c attribute like this:
var c = a1.getAttribute('c');Doing that is easy, but accessing node values is a little more confusing. Here's the code to access the editor node's value:
var editor = xmlhttp.responseXML.getElementsByTagName('editor')[0];
var editorValue = editor.firstChild.nodeValue;The firstChild method gives you access to a node's first child. But the editor node does not appear to have any children. Well, it apparently (or not so apparently) does. When attempting to access the value of a node, imagine the value text as being a node itself... access the nodeValue of the "text node" and you'll get the result you're looking for. Or, in more simple terms, always use .firstChild.nodeValue to access the value of a node.
Another XML Example:
<?xml version="1.0" encoding="UTF-8"?>
<across>
<a1 a="POET" c="Burns or Byron" n="1" cn="1" />
<a2 a="BLAB" c="Give everything away" n="6" cn="5" />
<a3 a="TALKS" c="Gives everything away" n="11" cn="9" />
<a4 a="ALSO" c="Too" n="16" cn="14" />
<a5 a="YOHO" c="Exclamation by Captain Jack Sparrow" n="21" cn="15" />
<a6 a="ELIOT" c="''A Cooking Egg'' writer" n="26" cn="16" />
<a7 a="LIAR" c="Ananias, for one" n="31" cn="17" />
<a8 a="PREY" c="Target in the wild" n="36" cn="18" />
<a9 a="ALONE" c="In isolation" n="41" cn="19" />
</across>
In the above example, all of the across child nodes are named differently, so it would be cumbersome to "getElementsByTagName" each one separately. Instead, take advantage of the childNodes collection to return an array of the nodes.
var across = xmlhttp.responseXML.getElementsByTagName('across')[0].childNodes;There is more to learn about XMLHttpRequest, but by simply knowing how to use the getElementsByTagName, getAttribute, firstChild and nodeValue methods and the childNodes collection, you'll have an easy time accessing all types of data from XML documents.
Before I close this lengthy post, there is one more thing I'd like to mention with regards to accessing XML files. It appears that you can not use Javascript to access local XML files using XMLHttpRequest, you can only access files found on a domain. I am completely perplexed as to why this is and if anyone out there has knowledge of the reason, or knows a workaround, I'd love to hear from you.
// Ryan Jennings
Labels: crossword, game, how to, iphone development, javascript, xml
MindComet at 8:09 PM - View Post
|
0 comments
@ 1:16 AM
Part 5: Importing Data From an XML File

Most programs, including games, require data. More often than not this data must be updated regularly so as to sustain the usefulness of the application. This type of data is usually stored externally (out of the application's main code) since it makes very little sense to require the application's user to download a new version of the program every day in order to receive the latest and greatest data. Therefore, with respect to our game, it makes a great deal of sense to store the data for each crossword puzzle in an external XML file. This way puzzles can easily be loaded individually by referencing a different XML file.
To import data from an XML file, we use the XMLHttpRequest Javascript object as follows:
var url = 'xml/070814.xml';
var xmlhttp = new XMLHttpRequest();
if (xmlhttp != null) {
xmlhttp.onreadystatechange = stateChange;
xmlhttp.open('GET', url, true);
xmlhttp.send(null);
} else {
alert('Your browser does not support XMLHTTP.'); }
}
function stateChange() {
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200) {
/*
A status of 200 means that the XML file was
loaded and parsed without error. At this point
you'll want to sort through all of the data so
that it can be used by your program. I'll give
you some tips on how to do this tomorrow.
*/
} else {
alert('Problem retrieving XML data.');
}
}
}
We'll talk a little more about reading from XML files tomorrow.
// Ryan Jennings
Labels: crossword, game, how to, iphone development, javascript, xml
MindComet at 1:16 AM - View Post
|
0 comments
Thursday, February 14, 2008 @ 8:15 PM
Part 4: Preloading Commonly Used Game Assets

Imagine playing The Legend of Zelda on your iPhone. Awesome, right? Well now imagine opening a treasure chest and finding this inside:
. What the hell? Looks like the image of your treasure hasn't loaded yet! (Okay, that's actually the icon to represent that an image can't be found, but play along with me.) Similar things could happen in your game if your assets are not downloaded by the time they need to be. Fortunately, there's a trick to prevent this from happening.If you've ever used Dreamweaver, you're probably familiar with the code that's used to preload images:
function simplePreload() {
var args = simplePreload.arguments;
document.imageArray = new Array(args.length);
for (var i = 0; i < args.length; i++) {
document.imageArray[i] = new Image;document.imageArray[i].src = args[i];
}
}
Look at the highlighted line. In Javascript you can create Image objects. By doing so, you can store images in the DOMs memory and quickly access them later. But preloading images in this fashion still requires an HTTP call out to the image so that it can be downloaded. When designing games for the iPhone, we want to limit the number of HTTP requests as much as possible. There's actually a way to embed some of your images in your Javascript file so that there's no need to download them.
If you've ever tried to open a GIF or JPEG in a text editor you'll notice that the guts of the image are a bunch of letters, numbers and symbols. There's a free program online, called the URI Kitchen that will read an image and print out the letter/number/symbol/gremlin guts for you.
To use the program, go to URI Kitchen, upload a file and then copy the resulting data from the address bar. Here's the data I got when feeding the program the above question mark image:
data:image/gif,GIF89a%10%10%D5*%80%C1.%8A%C9%2C%86%C5!o%B4%2B%83%C3-%88%C7%2C%86%C6%2C%85%C5%90%AE%D3j%92%C3)%7F%C0%26y%BC%8E%BD%E0%8A%AA%D0%92%AF%D44%7F%BF%87%A7%CFm%94%C4%25v%BA%84%A5%CE%7D%A0%CB%80%A2%CC*%81%C2%8D%AC%D2(%7D%BE%D3%E3%F1z%9E%CAs%99%C7%23s%B7%D3%E4%F1p%96%C6)%7D%BF'%7C%BEv%9B%C8%E3%EF%F7.%89%C8%C6%DD%EE%8C%B8%DCS%95%CA%B9%D6%ECT%98%CCF%91%C8*%83%C2%8D%BB%DDR%95%CA%9B%C2%E16%86%C2)%80%C0F%92%CA%2B%84%C4%9C%C6%E3%F1%F7%FB%8D%BC%DE(%7D%BF%24t%B8%25w%BA%23r%B7'z%BD%2F%8C%CA%22p%B5%94%B1%D4h%90%C2%FF%FF%FF!%F9%04%2C%10%10%06%AE%40%9EpH%2C%F2%1C%BE%A4r%B9t%20%7C%BA%A8t%1A%F5!.%BE%80%D6%608x%0D%23%AD%EF%D2%F0%15%0A1%0B%83q%92%C1%2Cg_%03%E2%13%08T4%D1%2B%E5%13)%EC%3E%10%13%3E%04%04(%2B5.%3E3%18%85%3E%13%15%3E%0A%1F%1F%18-%24%269%94%3E%15%14%3E5%A35%20%2C%25%0B%0B%20%A3%3E%14%1A%3E9%B19%12%0F%0F%12%B29%3E%1A!%3E7%BE7%1C%1D%1D%1C%BF7%3E!%1B%3E6%CB6%3B%19%19%3B%CC6%3E%1B%1E%3E8%D88%03%DB%03%D98%3E%1E%11%3E%3B%E4%E5%E6%E4%3E%11%09L%ECJ%09%3D%F0%F1%F2%F3%3DA%3B
Pretty, huh? Now, take that data, and use it as follows:
var questionMark = new Image();
questionMark.src = 'data:image/gif,GIF89a%10%10%D5*%80%C1.%8A%C9%2C%86%C5!o%B4%2B%83%C3-%88%C7%2C%86%C6%2C%85%C5%90%AE%D3j%92%C3)%7F%C0%26y%BC%8E%BD%E0%8A%AA%D0%92%AF%D44%7F%BF%87%A7%CFm%94%C4%25v%BA%84%A5%CE%7D%A0%CB%80%A2%CC*%81%C2%8D%AC%D2(%7D%BE%D3%E3%F1z%9E%CAs%99%C7%23s%B7%D3%E4%F1p%96%C6)%7D%BF'%7C%BEv%9B%C8%E3%EF%F7.%89%C8%C6%DD%EE%8C%B8%DCS%95%CA%B9%D6%ECT%98%CCF%91%C8*%83%C2%8D%BB%DDR%95%CA%9B%C2%E16%86%C2)%80%C0F%92%CA%2B%84%C4%9C%C6%E3%F1%F7%FB%8D%BC%DE(%7D%BF%24t%B8%25w%BA%23r%B7'z%BD%2F%8C%CA%22p%B5%94%B1%D4h%90%C2%FF%FF%FF!%F9%04%2C%10%10%06%AE%40%9EpH%2C%F2%1C%BE%A4r%B9t%20%7C%BA%A8t%1A%F5!.%BE%80%D6%608x%0D%23%AD%EF%D2%F0%15%0A1%0B%83q%92%C1%2Cg_%03%E2%13%08T4%D1%2B%E5%13)%EC%3E%10%13%3E%04%04(%2B5.%3E3%18%85%3E%13%15%3E%0A%1F%1F%18-%24%269%94%3E%15%14%3E5%A35%20%2C%25%0B%0B%20%A3%3E%14%1A%3E9%B19%12%0F%0F%12%B29%3E%1A!%3E7%BE7%1C%1D%1D%1C%BF7%3E!%1B%3E6%CB6%3B%19%19%3B%CC6%3E%1B%1E%3E8%D88%03%DB%03%D98%3E%1E%11%3E%3B%E4%E5%E6%E4%3E%11%09L%ECJ%09%3D%F0%F1%F2%F3%3DA%3B';
I can now access questionMark anytime I want to load that image in my game. And I can draw the image to the canvas (at coordinate 100,50) like this:
ctx.drawImage(questionMark, 100, 50);
Now, you won't want to use this approach for every image in your game. You'll still have to elegantly handle load times for large images. But this approach works great for smaller, commonly used images. What are the most commonly used assets in a crossword puzzle? The numbers used to label the grid, and the letters that will spell out each word. And those are the exact images that we'll "preload" into Javascript using this method.
// Ryan Jennings
Labels: crossword, game, how to, iphone development, javascript, preload, URI Kitchen
MindComet at 8:15 PM - View Post
|
0 comments
Wednesday, February 13, 2008 @ 12:50 PM
Part 3: Jumping Into Code

Okay, let's start programming this game. A couple of weeks ago... no wait, a little over a month ago... I posted about the canvas. The canvas is a really great way to draw to (or animate) a Web page. So, since the iPhone does not yet support Flash, utilizing the canvas makes a lot of sense.
Let's code the grid. There are two ways we can display it. We can use the canvas to draw the grid. Or we can create 225 17x17 pixel DIVs and float them all to the left within a 270x270 pixel parent DIV. The first option sounds easier, but we're actually going to do a little of both.
First we'll use the canvas to draw the representation of the grid. Here is some simplified code:
HTML
<div id="squares"></div>
<canvas id="crossword" width="269" height="269"></canvas>
Javascript
var ctx = document.getElementById('crossword').getContext('2d');
ctx.fillStyle = '#ffffff';
for (var i = 0; i < 225; i++) {
ctx.fillRect((i-Math.floor(i/15)*15)*18, Math.floor(i/15)*18, 17, 17);
}The code above will simply draw out 225 white squares, positioned exactly where we want them on the grid. It does not yet determine which squares should be black (the regions of the grid that are not part of the puzzle). We're ultimately going to read from an XML file to understand which squares are white and which are black. We'll get to that later.
The user will be interacting with the grid in order to read and solve clues, so we also need a separate clickable region for each of the 225 inner squares. That's where the second option comes into play. Let's add that logic to the for loop:
Javascript
for (var i = 0; i < 225; i++) {
ctx.fillRect((i-Math.floor(i/15)*15)*18, Math.floor(i/15)*18, 17, 17);
var newSquare = document.createElement('DIV');
document.getElementById('squares').appendChild(newSquare);
newSquare.className = 'square';
newSquare.id = i;
newSquare.name = i;
newSquare.onclick = function() {
// code to execute onclick
};
}
We've given each "pressable" DIV an id and name so that "onclick" we can figure out which clues they belong to. We'll get to that later as well.
For today, that is all. We're going to start slow and ramp up towards the end. Tomorrow I'll provide a neat trick for embedding images into Javascript so that you don't have to wait for them to individually download. A great way to preload images. It's very cool.
// Ryan Jennings
Labels: crossword, game, how to, iphone development, javascript
MindComet at 12:50 PM - View Post
|
0 comments
Wednesday, January 16, 2008 @ 11:28 AM
Hiding the Address Bar: Take 2
No one on this end has an iPod Touch, so we can't test to see if that's true or not, but I've had my own problems with this code in the past, so here are some additional things to try and keep in mind.
Try using the following code instead. It doesn't require you to add onload="hideAddressBar()" to your body tag and has proved more reliable in the past.
function hideAddressBar() { window.scrollTo(0, 1); }
Additionally, window.scrollTo(0, 1) does NOT seem to work if you include external CSS files using the following method:
Instead, do this:
@import "_assets/css/styles.css";
</style>
Hope this helps!
//Ryan Jennings
Labels: iphone development, ipod touch, javascript
MindComet at 11:28 AM - View Post
|
1 comments
Wednesday, January 9, 2008 @ 6:59 PM
Q&A: Can you hide Safari's lower navigation bar?
Hi,
Read your website with interest and wondered if you can help. ... My question is through additional code, how do you make the bottom 32px bar disappear please! I only want to ... utilize the absolute full screen as if in movie mode. ... Your help would be very much appreciated.
Thanks,
Chris
Unfortunately, I have bad news for you. It's currently not possible to hide that bar. I'm hoping that Apple will make some changes to how Safari on the iPhone operates in the near future. Perhaps they could tweak the browser so that the lower navigation bar only displays when the user is scrolling, and then slide back down and out of the way a second or two after the user stops scrolling or when the user starts clicking around the site. Additionally, it'd be nice if you could lock the page so that it doesn't move at all (if a page was designed to fit the screen exactly -- currently you can only prohibit horizontal scrolling, not vertical). Although, if scrolling was disabled... you'd never get back to the address bar (let alone a hiding lower navigation bar). Maybe instead of hiding the address bar completely, they can leave a sliver of it to display the page title. Clicking the title would slide back in the full address bar and navigation bar. I dunno. Apple, figure it out. Sorry I couldn't be of more help Chris.
If there are more of you out there with questions, feel free to email us at ask@iphoneminds.com. We look forward to hearing from you!
// Ryan Jennings
Labels: iphone development, javascript, question and answer, safari
MindComet at 6:59 PM - View Post
|
3 comments
Monday, January 7, 2008 @ 11:28 PM
Draw vector art using the canvas

We all know that the iPhone is incapable of displaying Flash content. But there are still two Flash-like benefits that we can take advantage of on the iPhone: vector art and animation. When Mac OS X 10.4 was released, Safari supported a new feature called the canvas. Safari, Firefox, Dashboard, and any Web Kit-based application (including the iPhone) can do arbitrary drawing of content using the canvas tag. This extension lets you reserve an area of your web page or widget and use rendering calls like those found in Quartz to paint complex paths and shapes in that area. The canvas tag was heavily utilized by the initial offering of Dashboard widgets released by Apple. Now you can use it to draw shapes to your iPhone webpages.
Don't get me wrong, this technique is not for amateurs. But if you're already familiar with using the Flash MX Drawing API, drawing using the canvas should be pretty easy. Additionally you can then use Javascript to continually redraw the canvas and animate your art.
Following is a silly example of what the canvas can do. Not very practicle, but go ahead and give it a whirl to see what it produces.
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Example</title>
<meta name="viewport" content="width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
<script type="application/x-javascript">
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
for (i = 0; i < 6; i++) {
for (j = 0; j < 6; j++) {
ctx.fillStyle = 'rgb('+Math.floor(255-42.5*i)+','+Math.floor(255-42.5*j)+',0)';
ctx.fillRect(j*25,i*25,25,25);
}
}
}
</script>
</head>
<body onload="draw()">
<canvas id="canvas"></canvas>
</body>
</html>
For more information on how to use the canvas, check out Mozilla's canvas tutorial. It's filled with static and animated examples. Enjoy!
http://developer.mozilla.org/en/docs/Canvas_tutorial
// Ryan Jennings
Labels: animation, canvas, flash, iphone development, javascript
MindComet at 11:28 PM - View Post
|
0 comments
Monday, December 17, 2007 @ 5:19 PM
Basic AJAX Scroll Animation w/YUI

Going through someone else's code is an art, you have to get into the developers mind and discover what he or she was thinking when writing the piece. While scavenging the Internet for an AJAX scroll animation, similar to that of the iPhone, I was so disgusted by the code I saw that I just wrote my own.
Some key things I had to keep in mind while writing this javascript code were, "How can I dynamically make one object slide off the screen while a second object slides onto the screen?" And "How can I keep users from going psychotic with my slider?"
To make the code dynamic, that was the easy part, I created a function calling out to the Yahoo UI Library (an AJAX source library). But to restrict when they user could slide the objects, that was the tricky part.
The YUI Library has three animation events that would allow me to limit when the slider would, well, slide...
- onComplete
- onStart
- onTween
It was actually rather interesting how these three events functioned together. Checking if the animation is active (ie. isAnimated();) proved to be a thought provoking process. The onStart function would always return false while checking isAnimated, because onStart does a check right when you start the animation, not yet animated but almost animated. The onComplete function did exactly the same thing while checking isAnimated, it's done being animated. While the onTween function checks for every single tween or frame, it always returns true.
You can download the following YUI files from the Yahoo Developer Site (http://developer.yahoo.com/yui/):
yui/build/yahoo/yahoo.js
yui/build/event/event.js
yui/build/dom/dom.js
yui/build/animation/animation-min.js
<script type="JavaScript">
var movethis,target,x,y,goodToGo=true;
var waitForIt = function() {
var onTheMove = this.isAnimated();
if (onTheMove == true) {
goodToGo=false;
} else {
goodToGo=true;
}
}
function slide(moveThis, x, y) {
if (goodToGo == true) {
moveThis = document.getElementById(moveThis);
if (x != null && y != null) {
var anim = new YAHOO.util.Motion(moveThis, {
points: { by: [x, y] } });
}
anim.onTween.subscribe(waitForIt);
anim.onComplete.subscribe(waitForIt);
anim.animate();
}
}
</script>
// Jay, aka W3prodigy
Labels: ajax, how to, iphone development, javascript, yui
MindComet at 5:19 PM - View Post
|
1 comments
Wednesday, November 21, 2007 @ 6:00 PM
Build it Yourself!

There are a few first steps you need cover in your code for an iPhone specific website or application. These keep the screen from zooming out and set the presentation to the best possible for the user.
Make your site fill the entire screen - no zooming
by default, the iPhone Safari browser zooms a webpage out to 25% of actual size. There are times when you may want it to zoom out or in for various needs, but for a starter, we're just going to show you how to make it zoom to a 1-to-1 ratio. This means that if you build the site at 320 pixels wide, a user will view the site at full width on load - no interaction required. If this seems unclear, please scroll down to the post about wallpapers and begin with the second Step 1 listed.
So, what do you need to do? Just copy the text below into your HTML - before the </head> tag and your ready to go.
One thing to note, this only covers the site in the basic portrait view mode. If a user turns the phone sideways, it will not scale to fit. We'll cover how to do that in a another post.
Hide that unsightly URL field
Okay, it's not really unsightly, but when dealing with limited space, every pixel (or 60 pixels in this case) counts. This one is as simple as the previous code snippet. Again, just place this before the </head> tag and your ready to go. On load the browser bar will still appear, but once the page has loaded it will slide up like magic and make your friends want to buy you expensive gifts. Seriously.
function hideAddressBar() { window.scrollTo(0, 1); }
</script>
These are just the very basics of developing for the iPhone. In the future you can look forward to full site templates you can download here that allow you to just drop in images and links and you're ready to go. We're good like that.
As one last helpful reference, we've supplied an image below that outlines the iPhone specs and dimensions when in browser mode.

* the alternate content dimensions are if you hide the URL text field bar using the code above.
If you've developed a site or application for the iPhone, let us know. We'd love to help you show it off. We also like to keep an eye on the competition. ;)
//scott
Labels: how to, iphone development, javascript, specs
MindComet at 6:00 PM - View Post
|
0 comments


