Chomper Stomping jQuery/JavaScript/CSS 3/HTML 5, Java/PHP/Python/ActionScript, Git, Chrome/Firefox Extensions, Wordpress/Game/iPhone App Development and other random techie tidbits I've collected

22Jun/102

CSS 3 Animation? Yes Please!

Ok, I'm so mega busy right now working on Flurl. I need to have it done TONIGHT because I've got other pressing obligations coming up and I just won't have any time to work on it after today.

I wanted to have movement and sound and I wanted it REALLY CORNY as an homage to the flickr pandas from whom it gets it's photos.

Here's a little preview (Webkit only).

That's just a little taste. It's going to be much more. It's going to have music and it's going to have a photo stream.

Just a little tid-bit though I wanted to explain how I animated it (It's a grand total of 62 lines from <html> to </html>). I didn't use any JavaScript at all, it's all CSS3 (amazing!).

First I designed the animation:

@-webkit-keyframes turnit{
from{
-webkit-transform: rotate(0deg);
}
to{
-webkit-transform: rotate(-364deg);
}
}

Yeah, really simple I know. I'm just telling it to rotate from 0deg to -364deg. Note that I named the animation "turnit".

Next I applied the animation to my div.

#turningBG{
-webkit-animation-name: turnit;
-webkit-animation-iteration-count: infinite;
-webkit-animation-duration: 30s;
-webkit-animation-timing-function: linear;
}

Line 1: use the animation "turnit" we defined before. NOTE THAT THE ANIMATION DEFINITION MUST COME BEFORE WE TRY AND APPLY IT. If I tried to use it before I defined it in my stylesheet, nothing would happen.
Line 2: Loop forever.
Line 3: Last for 30s.
Line 4: Make it smooth (linear). Could ease-in or out or whatever, but I wanted it to be seamless with no apparent start or stop.

Notes: If you look at the source you will see that I made my turning div 3200px wide and tall and then put it inside of a container that was 100% wide and tall and overflow hidden to keep there from being scrollbars. I then positioned the div so the center of it would be right under the unicorn's foot.

Here's some reference for css3 animation stuff. Here's some more.

Here's the entire "view source":

21Jun/101

Flurl – Part 1.a: Rolling your own JavaScript library, setting up the core

Flurl is a mashup I did recently as a practice exercise.

It takes a flickr panda photo-stream, displays a photo, and uses qurl to make a shortened URL link to the photo.

These are the notes I took while I was doing it.

The project can be found on GitHub at http://github.com/cmcculloh/Flurl

For this exercise I didn't want to use any JavaScript library. Normally I'd use jQuery (naturally) but I wanted to feel the pain of plain jane JavaScript again since it had been well over a year since I had done any AJAX without a library.

I decided I'd roll my own library that I could use to encapsulate the AJAX and DOM selection framework to keep it seperate from the actual app and to simplify my life in actually writing the app.

Since I wanted my library to feel a little jQuerish I decided as an homage I'd name it cQuery and use the _ instead of the $.

Step 1, the ubiquitous self executing anonymous function:

(function(window, document, undefined){})(this, document);

I'll break it down. The starting paren "(" and it's mate are just a "cool guy" coding convention to let people know, "this is weird! This is a library! This ain't yo mama's JavaScript!". It's the same as this:

function(window, document, undefined){}(this, document);

Which is simply just a function that immediately calls itself. The main reason to do this is to prepare our code for minification. When we minify, it will end up something like this:

(function(A,B,C){})(this, document);

So anywhere in our library where we had "window" or "document" or "undefined" it will not be the much shorter "A", "B" or "C", much smaller!

Paul Irish explains this in a *little* more detail in his 10 Things I Learned From the jQuery Source video.

Next we build the core function of our library, add it to the namespace and give it it's "_" shortcut:

var cQuery = function(elm){
};

window.cQuery = window._ = cQuery;

Note that if we didn't do that last line 'cQuery' would not be available in the rest of our JavaScript since it is hidden away inside of the closure we talked about above.

I really like the way jQuery works, and I want my library to mimic this. So calling:

cQuery("#domElementById").someMethod().anotherMethod();

ought to work.

Functions in JavaScript are just Objects that you can invoke. Functions can have their own methods, properties, etc. So basically cQuery is just an object that can DO something on it's own so we can say cQuery() instead of cQuery.doThing(). Much more convenient. So basically our var cQuery = function(elm){} code is just setting up my cQuery library object in a way that it can be called and passed the dom element we are working with.

Since I want to be able to "chain" things in my library, I'll need to add the methods in there that enable my chaining. I do this by ending my cQuery function with a return statement that returns an object containing the methods I wish to be available for chaining, each of these methods in turn returning an instance of the cQuery object (unless the method is specifically supposed to return something else, which makes it a destructive method because it ends my chaining), like in this example:

(function(){
var c = function(){
return{
blah:function(){
alert("blah");
return c();
},
blah2:function(){
alert("blah2");
return c();
}
};
};

window.c = c;
})();

c().blah().blah2();

That's it! Now I've got the core of my JavaScript library all set up, chaining enabled, library closed in but available on the namespace, all ready to be made useful! Here's the code we've got so far:

(function(window, document, undefined) {

var cQuery = function(elm){
//DOM selection and storage will go here

return {
//chain-able library methods go here
};
};

//make sure our library is exposed to the global namespace and make a shortcut "_" so we don't have to type cQuery every time.
window.cQuery = window._ = cQuery;

})(this, document);

20Apr/101

Using jQuery to bind a function to a select box change and retrieve the selected value

jquery-logo

If you need to bind a function to be called when a user selects an option from a select box using jQuery, you've come to the right place.

There are several different ways to skin this cat, but basically here is what we are going to do:

1. Bind a change event listener to the select box itself
2. When the box is changed, call a function that detects and retrieves the selected value

Here's the code:

$("#selectBoxId").change(function(){
var selectedValue = $(this).find(":selected").val();
console.log("the value you selected: " + selectedValue);
});

Let's break it down line by line.

Line 1:
$("#selectBoxId").change(function(){

$("#selectBoxId") grabs the DOM element out of the html and makes a jQuery object.
.change(function(){}); binds a function to the change event of the select box. Any time anyone changes the selection in the select box this function will fire off

Line 2:
var selectedValue = $(this).find(":selected").val();

var selectedValue creates a new variable that the selected value will be stored in.
$(this) creates a jQuery object based on the select box that triggered the function.
.find(":selected") looks a the select box that triggered the function and finds the option that got selected.
.val() gets the "value" of the selected option.

Line 3:
console.log("the value you selected: " + selectedValue);

this just calls the firebug console in firefox and tells it you want to print something out to it. You don't need this line, but this line shows you that the above code did indeed grab the selected value out of the select box.

Line 4:
});
This just closes the open change function started on line 1.

This code assumes that you have an HTML select box with an id of "selectBoxId". If you have a series of select boxes that all need the same function bound to them for some reason you can give them all the same class name (say "selectBoxesClass") and select them like so: $(".selectBoxesClass").

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Conforming XHTML 1.0 Strict Template</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js"></script>
<script type="text/javascript">
$(function(){
$("#selectBoxId").change(function(){
var selectedValue = $(this).find(":selected").val();
console.log("the value you selected: " + selectedValue);
});
});
</script>
</head>

<body>
<select id="selectBoxId">
<option>Foo</option>
<option>Bar</option>
<option selected="selected">Beh</option>
</select>
</body>
</html>

Here is the example

Here are some resources for further reading:
jQuery
jQuery change
jQuery find
jQuery val
jQuery :selected selector
jQuery Element selector
jQuery id selector
jQuery class selector
Firefox
Firebug
Firebug Console

12Feb/101

My own little dailyWTF

Note: WTF can stand for "Worse Than Failure"...

Yesterday I was re-living my saga on thedailywtf.com. I'm Jared L.

Part 1
Part 2
Part 3

Well, immediately after reading all of that I went and made a big 'ole wtf all on my own. Read on...

Every Wednesday and Friday finishline.com gets updated with new promotional material. Most notable of all of these is the "A1 rotator". This is the flash rotator prominently displayed on the homepage of the website. Each Tuesday and Thursday Sean in marketing will send me (up to) four new .swf files and one .xml file to drop in to the root directory of the site and the A1 rotator will magically update with these new files. I've been doing this bi-weekly for a year and a half and have never personally screwed it up. It's not hard. I copy the files from one directory into another and zip them up and send them over to the build and release manager, Brandon, who sends them over to Verizon who pushes them out to 71 different instances of our website. It functions like clockwork. Until it doesn't. This is a story about a massive multi-layered failure of the A1 rotator and it's 100% my fault.

Thursday afternoon at around 4:30 I received the files from marketing and copied them over into the required directory on my local box. As always, I manually uploaded the files to the QA server (a mirror of production) so marketing could test them. I zipped up the files and e-mailed them Brandon. I didn't bother to go out to our QA server and test them myself because, "hey, I've been doing this a year and a half and have never screwed this step up and I'm really busy and what could possibly be wrong? I just copied some files over, what could I have screwed up?" Oh the fallacy...

At this point, the piece of dung sorry excuse for an e-mail client "Outlook" crashed. I had already sent off the files so I didn't notice.

An hour later when I'm preparing to go home for the day I go to send and e-mail and see that once again Outlook has crashed. I pop it back open and find that I have an e-mail waiting for me from marketing. Five minutes after I sent the zipped files over to Brandon, marketing e-mailed me to let me know that something was wrong with the A1 spot on the QA server. Crap.

I open up Firefox and hit the QA server only to find myself staring at a big empty white space where the A1 rotator should be. WHAT???

Ok, maybe the xml file is pointing at a non-existant swf file and the whole thing is bombing out... What are the names of the files? Oh, there's a complicated one, "a1_jordan_02102010.swf" maybe the xml has that name screwed up. Let's simplify things. I rename the file "a1_jordan.swf" and edit the XML "a1_jordan.swf". Upload. Check. NOPE!

OK, marketing has obviously screwed this all up. Everyone from marketing has gone home at this point so I send an e-mail off requesting corrected files from marketing and prepare to call it a day. But I can't just leave it at that because that would be irresponsible.

I quickly scan the root directory looking to make sure all the swf files are there. Yes. Is the XML file there? Yes. Ok, so banner_rotator.swf should be pulling them in but it's crash... OOOOH NOOOOOO (said in a Tim "the Tool Man" Taylor voice). Suddenly I'm flashing back to the previous Thursday when I'm performing this exact same task. I recall that at that time I was feeling a little "cleany" and I decided that I should go through and remove all the old swf files from promos-gone-by. Sean happened to walk into my cube at exactly that moment and together we review the fifty or so swf files that don't need to be there anymore. Clicking them one by one was going to take too long, so I selected ALL of the swf files and then we went through and de-selected the four required for the next day's A1 rotator. Once I was sure I wasn't going to delete the rotator files, I went ahead and deleted the rest of the swf files. Including the banner_rotator.swf master file that pulls in the xml and the four content swfs. SHOOT!

So I recover the banner_rotator.swf, add it to the root, and send it all back over to Brandon. I follow up with an e-mail absolving marketing and I call it a day. Everything's fine and I even let my boss know what happened assuring him that everything is taken care of. Little do I know...

The next morning as I'm preparing to leave for work I happen to check my work e-mail. The A1 rotator is missing from the site. Apparently the promo push still hasn't run, and the CDN we use has dropped the banner_rotator.swf file since it no longer exists in our build (because I deleted it last Thursday so it was dropped out of the following Thursday's production deploy, which was yesterday). Great. The promo push is about to start though and then everything will be ok. I call a few people and send some e-mails to let everyone know what's going on and leave for work.

When I get into the office I pull up the website and check only to discover that the first spot in the rotation is completely blank. I have several e-mails informing me of this as well. Flash back time... Remember when I renamed the jordan file as part of my debug process? Yeah, me too. Apparently I never put the name back correct but the xml did get switched back. So now I really am missing a swf. I guess the only thing that happens if a swf is missing is that the spot for it in the rotation is just blank. Good thing the only spot that's blank is the very first spot, which is only seen by 100% of our visitors and only clicked on more than anything else in the entire site!

I call Brandon and sheepishly request that he manually go through all 71 instances of our site and rename the file in every one so that we don't have to do an emergency push and incur a fine. Which he does and the whole thing is fixed.

At this point I get up and start looking for my coffee mug. It's missing. I finally stumble across it right next to the coffee machine. Curiously it has a little pile of cream and sugar in it all ready for a cup of jo. It is then that I realize what the root of the whole problem was. I never had my coffee the day before. Why? Because someone took the last of the coffee and didn't start a new pot brewing; when I went to get my coffee the pot was empty. I had started a new pot brewing, got my mug ready, walked away and never came back; resulting in me doing the entire promo push without any caffeine in me. Apparently I just suck without caffeine. And that boys and girls is why you should always start a new pot if you drink the last of the current one. That is also my lame attempt to shift the blame away from myself, lol. No, this was 100% my fault...

10Feb/101

JavaScript Arrays – new Array() vs array literal

1306092411709_a923d

Do not every say new Array() when creating a new JavaScript Array(). There are differences between creating an array in Javascript using the array literal, or the fully qualified "object" name (Array() vs []), and the bottom line is that the "right" way to do it is "[]" (array literal) instead of "new Array()". Here is why:

25Jan/100

TOTW: Google Closure JavaScript Compiler Web Interface

This week's Tool of the Week is Google Closure JavaScript Compiler. I know everyone has probably heard of it, but if you haven't gotten around to checking it out, this post is great for you.

What it does:

Allows you to compile JavaScript down to a compressed form. It reads in your JavaScript, parses it, and re-writes it to be much smaller. This is good for you because it will make your site load faster, much faster in some cases.

When you need it:

  • To compress your JavaScript
  • To combine multiple JavaScript Files

How to use it:

The Example:

Additional Notes:
Notice in the compile directions there is an @output_file_name parameter. You can change this to something other than "default.js" (you can name it anything you want. Then you can actually access the results by clicking on the link on the right hand pane that says "The code may also be accessed at default.js" (if you rename it it will put the name you put instead of default.js). Here's the file generated by my example.

30Dec/090

JavaScript, pass by value & pass by reference

In JavaScript Objects are passed by reference. Primitives are passed by value. I did not know this. I should have, but I didn't. I didn't because I had never done anything destructive on an object before today. Let me elaborate:

Outputs:

3
4

Ouputs:

b 4
b 4

Which means if you do this:

This is the result:

something [Object y=2, Object z=fred, Object a=9, 2 more... 0=Object 1=Object 2=Object 3=Object 4=Object]
something [Object y=2, Object z=fred, Object a=9, 2 more... 0=Object 1=Object 2=Object 3=Object 4=Object]

For those of you slightly newer to JavaScript than me, shift() removes and returns the first element in an array. So, in our "something" array in our object, "x" is stripped out, not only in the copyData object, but also in the data object. This is because both copyData and data are simply REFERENCES to an anonymous object containing an array called "something". Crazy.

Today I was trying to make a copy of an object, and then call shift() on an array inside of the copy. I was baffled when the original object was being affected.

There is no good way to make a true copy/clone of an object in JavaScript. John Resig has a jQuery solution that can be found here: http://stackoverflow.com/questions/122102/what-is-the-most-efficent-way-to-clone-a-javascript-object

28Aug/092

Finding the number of hex tiles inside of a hex shaped grid…

EDIT: There is a mathematical formula that is much better than the method I illustrate below. It is: 3n^2 - 3n + 1

A huge thanks to Sam Hughes for very patiently helping me out with this formula. He's the best mathematician I know...

Here's the JavaScript code to execute this formula (just change maxTiles to be however wide your hex grid is):

	var maxTiles = 7;	
	var n = Math.ceil((maxTiles - 2) / 2);
	var numberTiles = 3 * Math.pow(n, 2) - 3 * n + 1;

Below is the original post:


This is kind of like finding the area of a hex grid, except that for some reason none of the formulas I found for doing that would work. Also, finding the area of a triangle made of hex grids won't work with the normal triangle area formula either.

What I am illustrating here is how to find out how many hex tiles are inside of a hex shaped grid of hex tiles.

Here is a picture of the grid:

What you have to do is:

1. Take the half the width of the hex grid rounded down. So, if the hex grid is 7 wide, you take half of seven, 3.5, and round it down to 3. This can also be called "flooring" the number.
2. Take that number and use it to seed a floor loop, which counts down from that number, adding the new number to a grand total each time.
3. Multiply that grand total by 7 and add one for the middle tile (which never got counted).

Here is the code:

	var triangleTotal = 0;
	for(var spaces = Math.ceil((maxTiles-1)/2); spaces > 0; spaces--){
		triangleTotal += spaces;
	}
	var triangles = triangleTotal * 6;
	var numberTiles = triangles + 1;
	return numberTiles;

You'll notice that I took the total minus 1 divided by 2 and "ceil"ed it, instead of taking the total divided by two and "floor"ing it. It doesn't matter which you do...

Here's a visualization proving this out:


I only took one math class in college, otherwise this would have probably been very obvious to me as it may seem to you. There is probably even a really fancy easy little formula for this, but I don't know it and couldn't find it...

Here's a page where you can test this formula out in action. Note that a hex is always going to be an odd number of hexes wide, so if you enter an even number and get a funny shaped hex, this formula isn't going to work...

27Aug/092

JavaScript Haxagon Game Board with Hexagon tiles

I'm working on a JavaScript version of The Settlers of Catan. The first problem I decided to tackle was constructing (visually and array-structure wise) the game board using nothing but loops (didn't want to hard code it).

This ended up being much more difficult than I thought it would be.

I am going to split this up into two parts:

1. Setting up the game board visually
2. Setting up the array structure

Creating a Hexagonal game board with hexagonal spaces in JavaScript with for loops:

For this to work properly, you must have an even number of columns and rows. I chose seven since that is what makes up the Settlers of Catan game-board.

You have to split the building of the board into three stages:

1. The expanding stage
2. The apex or middle of the board
3. The contracting stage

I placed everything in one large for loop that counted off the "rows".

I figured out that while the rows are less than half the max rows divided by two (and rounded down) the board would be contracting. The number of tiles you would start with would be equal to the maximum number of tiles divided by two, rounded up, plus the current row number.

Once the number of rows equals exactly half the number of maximum tiles (rounded down) you are on the middle row. In this case the number of tiles is the max number of tiles, no math required.

Otherwise, you are in the contracting stage and the number of tiles you need is equal to the maximum number of tiles, minus the current row, minus half of the maximum rounded down.

Sound complicated? That's because it is. This took me about 30 minutes to get straight in my head after studying the game-board shape, tiles, and making several diagrams and sudo-coding and then writing the actual code and fixing bugs (and deciding if I had to round up or down at certain places).

Here is the actual code:

<html>
<head>
<style>
img{
	margin-top: -18px;
}
#board{
	padding: 50px 25px;
	background-color: #000;
}
</style>
</head>
<body>
<div id="board">error loading</div>
</body>
<script>
function boardSetup(){
	var max = 7;
	var html = "";

	for(var row = 0; row < max; row++){
		if(row < Math.floor(max/2)){
			//place spacer tiles
			for(var space = 0; space < (Math.floor(max/2) - row); space++){
				html += '<img src="spacerTile.png" />';
			}

			//place game tiles
			for(var cols = 0; cols < (Math.ceil(max/2) + row); cols++){
				html += '<img src="tile.png" />';
			}
			html += "<br />";
		}else if(row == Math.floor(max/2)){
			for(var cols = 0; cols < max; cols++){
				html += '<img src="tile.png" />';
			}
			html += "<br />";
		}else{
			for(var space = 0; space < (row - Math.floor(max/2)); space++){
				html += '<img src="spacerTile.png" />';
			}
			for(var cols = 0; cols < (max - (row - Math.floor(max/2))); cols++){
				html += '<img src="tile.png" />';
			}
			html += "<br />";
		}
	}

	document.getElementById("board").innerHTML = html;
}

boardSetup();
</script>
</html>

Here are the images that go with it:

Here is the tile:tile
Here is the spacer. This one is completely invisible: start->spacerTile<-end

Here is the code running on my site.

10Jun/098

Creating Dynamically Re-sizable Buttons for iPhone Apps

Creating buttons that resize/stretch themselves automatically for your iPhone Applications is pretty easy.

Buttons are created as .png files with three parts: Left end-cap, middle (the stretch-able part), Right end-cap. Basically, you create a little tiny .png file with the left and right end caps (or top and bottom) and a little bity middle part that the iPhone will automatically re-size to fit any width or height you make the button.

I'll walk you through the process step by step and show you how to assign the image to your button.

If you are wondering how to get your Round Rect Buttons to look like the ones in the official iPhone apps (like the ones apple makes), this tutorial will show you how. Instead of creating your own button image, just use the ones from Apple, or download the zip file for this tutorial (the ones from Apple are in there too)

Creating the Stretchable iPhone App Button Image

To start with, you will need to actually create the button image. I used Fireworks to do this, but you could use Photoshop, Illustrator, Inkscape or Gimp if you are so inclined. I recommend Fireworks.

I wanted my button to mimic normal iPhone buttons, but also illustrate the concept of a "stretchy" button. So I made a square button with rotated square corners.

  1. Create a new image 30 x 50 (or there-abouts)
  2. step1

  3. Make sure to set the canvas color as "transparent"
  4. Mock-up your button shape
  5. step2

  6. Use the pen tool to trace it into a single shape
    step3

    I take the individual components from the last step, group them together, and lock them, and then trace around them with the pen tool. There's probably a better way, but this is the way I know. Use the guides to help you tweak it until you are satisfied

  7. Trace a smaller version of the button inside of the outline using the pen tool
  8. Fill in the inner button, and remove the line on it
  9. Fill in the outer button
  10. step4

    If the shape looks how you want it at this point, move on, otherwise, rinse and repeat...

  11. Color the button the way you want it
    step5

    The way I do this is to fill the outside shape with a dark grey (at the top) to light grey (at the bottom) linear gradiant with a 1px solid dark grey border (a little darker than the darkest part of the gradient). Then fill the inside shape with a custom linear gradient. This is the part that requires finesse. If you want it to look like an Apple button with the slick horizon line, start light, get progressively darker, and then in one pixel make a stark jump to the darkest shade of the color you are using, then get progressively light again.

    step5gradient

  12. Add a drop shadow to the inner shape
    step5dropshadow

    Click the inner shape. Click the "+" next to "Filters" in the Properties inspector. Select "Shadow and Glow > Drop Shadow". I've selected a distance of 2, Opacity of 100%, Softness of 1 and Angle of 267.

  13. Decide on the stretching points of your button

    Pick a point in the middle of the image where if that one pixel got stretched (repeated over and over again) to widen the button, it would look good (I chose 15px for the button I've been creating for this tutorial). Pick a point in the middle of the image where if that one pixel got stretched to lengthen the button, it would look good (I chose 27px for the button I've been creating for this tutorial). Make note of these two points as you will need to know them later.

  14. At this point you have completed the "up" state for the button.

  15. Save your file (I saved as "customButtonImageDefault.png")
  16. "Save As" and rename your file for the pressed or "down" state for the button (I saved as "customButtonImageDown.png")
  17. Choose a color for the downstate, and change each gradient point to a shade of that color.
    step6

    It makes it easier if you click the color wheel in the color chooser and use the slider on the right. Also, once you have chosen your base color after clicking the color wheel and adjusting the slider, click "Add to Custom Colors" so you don't have to re-find your base color again and again each time. You can just select it from the custom colors list at the bottom left of the color wheel menu and then use the slider to choose another shade of that color. Make sure not to move any of gradient points or they won't match up with the points you took note of earlier. Up states and Down states can have different stretch points if you want since you define each state's stretch points individually in the code, but it makes it easier if you just use the same points for up and down.

  18. Once you have edited all of the points. Open the images in fireworks and switch back and forth between them to compare how it's going to look when the button is pressed. When satisfied, save. Now you're ready to get to coding!
  19. custombuttonimagedefaultcustombuttonimagedown

Creating a Custom Re-sizable Button in an iPhone App

Ok, now that you've gotten your button created, it's time to implement it.

I expect you to already know how to create I phone apps, but if not, here are un-annotated pictures of me building the scaffolding to hold our custom buttons. Following the images are the explanation on how to use custom buttons.

Creating the Scaffolding to place the custom button in

Again, if you already have an app you are trying to use the custom button in, skip this step...

buttontutorial1buttontutorial2buttontutorial3buttontutorial4

Making the Custom Button in the iPhone App

  1. Open the interface builder and click on the button you would like to customize
    buttontutorial5
  2. Open the "Button Attributes" pane (Apple + 1)
  3. For "type" select "Custom"
    buttontutorial6

    At this point, if you didn't give the button a title, it will disappear (which is ok). If you need to find it and can't, in the main window (titled yourFileName.xib) there will be several objects. Expand the one that says "View" (or wherever you put your button) and you should see your button. Clicking on it there will highlight it in the layout window.

  4. Hold down "ctrl" and drag from the "File's Owner" object to the button.

    This can all be done from within the .xib window by switching to "list" view mode

  5. In the little context menu that pops up, select "customButton" (or whatever you called the button in your code)
    buttontutorialconnectbuttontooutlet
  6. Switch to the "size" window (Apple + 3)
  7. Set the size to at least 100 x 100
    buttontutorialbuttonsize

    This is just so we can see our image stretch. When you are doing this in a non-tutorial setting, you will probably not set this at all.

  8. Switch to the "Attributes" window (Apple + 1)
  9. In the "View" section (at the bottom), click on "Background"
    buttontutorialclickbackground
  10. In the "color" window that pops up, set "Opacity" to "0" (zero)
    buttontutorialopacity
  11. Switch back over to Xcode
  12. Locate your button files and drag and drop them into the "Resources" folder of your project
    buttontutorial7

    You will probably want to make sure and click the "Copy items into destination group's folder" button

  13. Click "Add" in the pop-up that appears
  14. Switch to your view controller file

    (the one that corresponds with the view you were just editing in the Interface Builder. The one that ends with ".m")

  15. Add the following code to the -(void)viewDidLoad{} method:

    buttontutorial8

    UIImage *buttonImageNormal = [UIImage imageNamed:@"customButtonImageDefault.png"];
    UIImage *stretchableButtonImageNormal = [buttonImageNormal stretchableImageWithLeftCapWidth:12 topCapHeight:0];
    [customButton setBackgroundImage:stretchableButtonImageNormal forState:UIControlStateNormal];

    UIImage *buttonImagePressed = [UIImage imageNamed:@"customButtonImageDown.png"];
    UIImage *stretchableButtonImagePressed = [buttonImagePressed stretchableImageWithLeftCapWidth:12 topCapHeight:0];
    [customButton setBackgroundImage:stretchableButtonImagePressed forState:UIControlStatePressed];

    The first block of code defines the image for the normal (up) state of the button. The second block defines the image for the pressed (down) state of the button. The first line of each block loads the button image for that state into the app. The second line of each block you can set the stretch points of the button (you should have noted these earlier when you made the button). You can keep setting these to different things and viewing the result to refine until you like what you see. The third line assigns the now stretchable image to the actual button. If you have more than one button you want to assign the image to, just copy the third line and replace "customButton" at the beginning with the name of the next button you want to assign the image to:


    UIImage *buttonImagePressed = [UIImage imageNamed:@"customButtonImageDown.png"];
    UIImage *stretchableButtonImagePressed = [buttonImagePressed stretchableImageWithLeftCapWidth:12 topCapHeight:0];
    [customButton setBackgroundImage:stretchableButtonImagePressed forState:UIControlStatePressed];
    [anotherCustomButton setBackgroundImage:stretchableButtonImagePressed forState:UIControlStatePressed];
    [thirdCustomButton setBackgroundImage:stretchableButtonImagePressed forState:UIControlStatePressed];

    In this way, you can assign the image to as many buttons as you want.

  16. Your viewDidLoad method should now look something like this:


    -(void)viewDidLoad {
    UIImage *buttonImageNormal = [UIImage imageNamed:@"customButtonImageDefault.png"];
    UIImage *stretchableButtonImageNormal = [buttonImageNormal stretchableImageWithLeftCapWidth:12 topCapHeight:0];
    [customButton setBackgroundImage:stretchableButtonImageNormal forState:UIControlStateNormal];

    UIImage *buttonImagePressed = [UIImage imageNamed:@"customButtonImageDown.png"];
    UIImage *stretchableButtonImagePressed = [buttonImagePressed stretchableImageWithLeftCapWidth:12 topCapHeight:0];
    [customButton setBackgroundImage:stretchableButtonImagePressed forState:UIControlStatePressed];

    [super viewDidLoad];
    }

  17. Now hit Apple + R to compile, run and view the output!
    buttontutorialcomplete

A good amount of the information for this article came from
Beginning iPhone Development: Exploring the iPhone SDK. It's an excellent book. By far the best iPhone development book out there right now. It's really easy to read and understand. Each chapter is one or two example projects from start to finish, step by step, wonderfully explained. After buying this book I had my first iPhone App up and running within a week. The thing they don't talk about (that I did) was actually creating the button image, and dealing with the opacity. Again, all of the files for this tutorial can be downloaded from here. Feel free to leave any feedback/questions/whatever in the comments... Good luck!