Rob Marini: Good Afternoon.
Welcome to Session #305, Designing Apps with Interface Builder.
My name is Rob Marini.
I'm an engineer on the Interface Builder team and I will be joined on stage later today with Barry Langdon-Lassagne, one of my colleagues.
It's been twelve months since the last time we all got together and talked about Interface Builder and what it was and how to use it and what a year it's been.
First, our platform expanded with the release of the iPad and then matured to unprecedented levels with the release of iPhone 4 and iOS 4 and today we're going to talk about how to take advantage of those two products.
So, one way you can do that is by using something that we refer to as a universal app.
It's one binary that can be run on any device that runs the iOS and if you have an iPhone app and you're considering writing an iPad app, this lets you take advantage of your existing customer base.
All of your iPhone customers who purchase an iPad can just use your iPad app and just that easy.
They plug into iTunes, it syncs over, it's great and if you're writing an iPad app, by making it universal and having an iPhone version you're exposing yourself to a larger audience.
Anyone with an iPhone can also purchase your app.
As an added perk since it's a single application, it's a single apps to review.
You're one review away from all of your customers on all of our devices having the latest and greatest of your product.
The other road you can take is to create two separate apps from the same source base and this is useful if the two applications are very platform specific.
Maybe you have some sort of content creation app on the iPad and a reader of some sort on the phone.
This is you know the really type of thing that this is great for.
Or maybe you want to take advantage of per-device pricing.
Maybe your iPhone app has less functionality than your iPad app and you'd like to offer it to those customers at a lower price and maybe you have a game and it has lots of high-resolution images and you want to make sure your iPhone customers are only getting the content that they need for their game to run.
So this is a quote I came across in a blog a few days ago, Universal Apps are far more appealing to users than apps that target just one platform.
This is from the developers of iTeleport.
It's a screen-sharing app for the iPhone and the iPad, and they actually went the route of having two separate applications and then later created a Universal App and you know they have some numbers and all kinds of quotes and stuff on their blog.
I'd encourage you to read it and you know if you're thinking about going to the route of two separate applications this might change your mind.
So now that we decided that we're going to create a Universal App what does one look like?
Well, this is what an iPhone app usually looks like.
You have the core functionality of your app in the infrastructure.
You probably have some resources and you have your user interface, the things that the user touches, gets to interact with and really comes to care about.
Well, what do two separate apps look like?
They look something like this.
Same infrastructure and resources and two separate user interfaces packaged as two separate applications.
Let's make it universal.
Well, we had to take advantage of the fact that it's a single app so we're sharing the infrastructure.
We're sharing the resources and just the things that need to be platform specific are the UI.
Let's peek into each of these for a bit.
So first the infrastructure.
This is what allows your app to function.
It's the core, the heart and it's the thing that's going to be the same no matter where your application is running.
You probably also have a lot of resources that you can share between the iPhone and the iPad.
Maybe you have some TableView Cells that you've designed in Interface Builder.
You can easily load those on both platforms.
Maybe you have some images and some data that you used to start your application up, some simple views in view controllers.
All of these things can be shared and then of course the core through UI.
These are the things that really need to be targeted for the platform they're being displayed on.
You have to give your user the best possible experience when they're running your app and this is how you do it.
So when you're going through the process of creating a Universal App, you might wonder well should I share this class between all my platforms or should I create two separate ones, one for the iPad and one for the iPhone and so when you're thinking about creating a per-platform class the reasons you should do it are you know.
It serves a specific purpose on a platform.
Maybe it's a view controller that manages a view, the design is very different between the iPhone and the iPad, and maybe you have a class that's customized and tailored for the specific capabilities of a particular device.
You only really want it to run in that device and maybe you have some code that you want to make sure will only ever execute on a particular device.
This is one way to do that.
When do you share your classes?
Well, a lot of our code has very little to do with the platform and when it does we probably just want to change the behavior of something a little bit and so you can use some runtime checks to change that behavior.
You know, you could say am I running an iPad?
If I am, do this and we'll show an example of this later in the Demo.
So how do we adopt the features of the iPad?
How do we go about creating a Universal App?
Well today, this is the application we're going to use.
It's a very simple recipe catalog and behaves like most iPhone apps.
You tap on a TableViewCell, a new view slides in.
Let's look at the structure of this.
We have a navigation controller and that's managing a stack of view controllers, pretty simple.
We have a list of recipes and some details about them.
Let's make a run at the iPad.
Well it's probably going to look like this.
We've decided we wanted to use a SplitView Controller.
This is a class that's new for the iPad and it is designed for the UI of the iPad.
It presents two view controllers on the screen and it is really fantastic.
Lots of our apps use them.
Lots of third-party apps use it and so on the left side here you can see we've just taken part of our iPhone app and put it there.
It's the same code, on the right we have the information about the recipe, we've designed it and tailored it specifically for that platform, and this is it running on the iPad Simulator.
So when we look at our project we have to decide what we want to share and what we want to separate.
So things that we want to share our models and TableViewCells, TableViewController.
We have a Navigation Controller that's not even a subclass of UI Navigation Controller.
It just manages a stack.
It's the same across platforms and the real key to our UI is our, the detail of our recipe.
The information about it so that we want to target for each platform and we have our MainWindow and our AppDelegate which allow us to present that to the user so we're separating that based on the platform that we're on.
Then we also have a SplitView Controller which is iPad only.
So how do we create a Universal App now that we know that we do?
Well, in Xcode you can go to your side of your project and right-click on the target.
It gives you an option to Upgrade your Current Target for iPad and when you click that a sheet drops down and you have two options.
These are the two things that we talked about earlier.
You can upgrade your current target for iPad or you can choose to create two separate apps.
This is the point where you make that decision and today we're going to create a Universal App so we'll pick that.
Then you need to upgrade your user interface.
So if you have some Interface Builder documents you have two options.
You can open them one by one in Interface Builder and under the File Menus select Create iPad version and save them to disc or you can batch process all of your Interface Builder documents on the command line and there's a tool that's documented in the release notes that you can consult.
We're not going to talk about it here.
Now that you've done that it's time to think about what your user interface is going to look like.
You should probably adopt your UI paradigms.
You don't want to give your iPad customer a giant iPhone app on that device, probably want a SplitView Controller if you've been using a Navigation Controller and you know you can consider at this point what you can share between the two platforms and we've decided we're going to use a SplitView Controller and we have this TableViewCell that we use and we think we can share between the two.
So now I'd like to show you taking our existing iPhone app and bringing it to life on the iPad and here we have our application.
Build and Run show you it works.
Got some recipes here.
Can select one.
Great. Quit the simulator for now and I'm going to Right-click on the target and select to Upgrade for iPad and as I've said I'll pick a Universal App.
OK. This has gone ahead and created some files for us.
We talked earlier about what files we were going to keep shared and what we're going to split up across the platforms.
So I've already done that.
Going to go ahead and launch that project and what I've done here is to take a RecipeDetailViewController, our AppDelegate and just copied them and rename them.
I haven't actually touched any code.
So the first thing we're going to do is to Open our iPad version of our MainWindow and you can see this is the iPad version of a window.
I mentioned we were going to take advantage of the SplitView Controller so I'm going to drag one in.
You can see it there and if you disclose the contents of the SplitView Controller you can see it contains a navigation controller.
Well we already created one for our iPhone app so I'm just going to replace it by dragging it over and you can see there it is.
So that's the left side of our SplitView Controller.
Now we want to talk about the right.
So I'm going to go back to Xcode and I have an iPhone Interface Builder document which is our RecipeDetailViewController.
I'm going to go ahead now and create an iPad version of this.
There we go and so we knew on our iPhone application that we were running inside of the context of a navigation controller.
That's not the case any more.
So, I'm going to get rid of our simulated top bar and I'm actually going to put a toolbar up there so I'll drag one out of the library, put it up here and since the toolbar is on the top, I want to pin it there for when we rotate and turn off any to the bottom.
Going to move things around to make it look a little better, get a bigger image of you.
That's a little too big.
Let me make it 300.
Make this text view a little wider and move this one down to the left.
Now earlier we were running inside of a Navigation Controller so we got to put our title in the NavBar.
We don't have a NavBar any more so I'm going to drag in a label and I'll put it just up here, make it pretty big, and Helvetica it's a great font but it's not the most eye-catching font.
I'm going to do something much more dramatic.
I am going to pick Zepthino [assumed spelling] and let's make it oh 52 high.
We want our min size to be something a little bit bigger, maybe 27.
We want to center it.
Let's give it a shadow, offset the shadow by 1 and we'll make the text white.
Great. When we rotate we want the label to resize so we'll turn that on here.
So now we need to save this.
So we're going to go to my desktop where I have this project and select my prebaked converted one.
Select Resources iPad and let's give it a name.
Cool. Now Interface Builder hopefully notices that there's an Xcode project in that file structure and asks if you want to add it to your target.
As a matter of fact I do so I'm going to say Yes.
Add it. So now when we go back to Xcode you can see it appears here.
I'm going to drag it up just to keep things organized and previously I had duplicated the file for the view controller that managed this view and since I've added new things in my UI I want to create Outlets to them.
I'll do that now.
I have a toolbar and a label.
Create some instance variables to back them.
Let's jump to our Implementation File.
We want to synthesize our properties.
Get rid of that extra space.
Clean up after ourselves and now I have this method configure view and this gets called every time that the recipe changes so here I want to set the title of a recipe, just like that.
Go back to IB and we want to connect these Outlets.
Our file's owner is still our old iPhone version of this class so I'm going to make that be the iPad version just by changing the class in the Identity inspector and now if I Control click on it you can see I have the connection hud.
I'm just going to drag connections to the label and to the toolbar and that is the UI we designed for the iPad.
I'm going to close this now and go back to our MainWindow because we set up the left side earlier.
Now we need to set up the right.
So, presently it's an instance of UI View Controller, we want to make it an instance of the View Controller we just designed the U4.
So I'll do that here and I've added a SplitView Controller.
I've moved around the Nav Controller and I put in this RecipeDetailViewController.
I'd like to have Outlets to all of them and so previously our AppDelegate had the Outlets to MainWindow.
I've created a version for the iPad.
As you can see it's the same code as for the iPhone, just renamed.
That's not the right file.
That is. So I'll put in my properties, my instance variables, need to declare that class and now we'll synthesize the properties, release the instance variables and there's this method application to finish with options.
This is what gets called when your app launches and it adds the subview of a view controller to your window.
Previously we added the view of the navigation controller.
We want to use our SplitView Controller so I'm going to go ahead and select that and replace it with SplitView Controller.
So now we need to go back to Interface Builder and connect these.
So here's the AppDelegate and the same thing like we did earlier, we go to the Identity inspector and just replace the name of the class to be our iPad version and we can Control click on it and drag connections to our DetailViewController and to our SplitView Controller.
Go into IB, Xcode rather and I'm going to simulate this on the iPad Simulator so Build and Run and as you can see this isn't quite what we had in mind.
Nothing is actually showing up on the screen when you hit the button in the toolbar.
If you're familiar with any iPhone apps like Mail or if you've used a bunch of their Party apps generally the way that a SplitView controller works is when you're in landscape it shows the View controller on the left and the right.
When you rotate to portrait, the one on the left animates out and this button appears on the top left that when you tap on it, it shows you the View controller that was previously on the left.
We want that behavior and if we rotate the simulator you can see we have our recipes on the left like we expected.
When we tap on them doesn't quite behave the way that we wanted.
I don't think we had this design in mind.
We didn't want it to slide in on the side.
We wanted it to replace the area in the middle.
So I'm going to quit the simulator and now we're going to add some polish and make this a really great app.
So I have a class which is RecipeTableViewController and there's a method in here called did TableView did Select Row at index path and this gets called every time that you tap on a TableView cell.
So I'm going to write some code in here and what we're doing here is checking to see we're asking the device if we're an iPad.
If we are, we're setting the Recipe that's displayed by our RecipeDetailController.
Otherwise we want to take the code that we previously had there, just cut it, move that over and paste.
So now if you're on an iPhone and you select TableViewCell, it'll push a new view controller onto the stack like it did before where if you're on an iPad it'll just show it on the right.
The next thing that we wanted to do was to get that button on the top left to work.
So to do that, we're going to take advantage of the fact that the SplitView Controller class has a delegate protocol that we can implement and two of the methods from there are called when the view on the left shows and hides.
So, that's going to be where we're going to configure that button.
So I'm going to make my Detail View controller be the delegate for that and I'll add an instance variable for a popover.
Maybe I'll add a property in case I want to access it from someplace else inside of the M file.
I want to synthesize that and take care of releasing it.
We also now need to write some code.
As I mentioned there were two methods that we were interested in.
These are them.
SplitView will hide View Controller with bar button item for popover controller and SplitView Controller will show View Controller in validating bar button item and here we're setting the title of the bar button item.
We're giving it a pop we're taking the popover that's passed into us by this method, setting our popover to be that and hiding it when the view on the left shows.
Great. The other thing that was wrong with our app was when we first launched, we wanted a recipe to show up, not just white.
So view did load is the method that gets called the first time the view on the right is shown so I'm going to go ahead and in here set our recipe to be the first one in our recipe list.
Great and the final thing that I need to do is back in Interface Builder I need to let the SplitView controller know that the RecipeDetailViewController is its delegate.
I'm going to do that by Control clicking on SplitView Controller.
You can see there's a delegate item here and I'm just going to drag a connection from that to the DetailViewController and I'll Save.
Go back to Xcode and Build and Run and great.
You can see that works.
We have our app.
We're seeing a recipe.
We tap on the button, we can see the popover, take a recipe, here's some cheese that I made the other day, maybe a BLT.
Could go for some right now.
Recipes are still there on the left.
Works. Let's quit the simulator.
Go back. Pick the iPhone and Build and Run and you can see it still works on the iPhone just like it did before.
Let's go back to the slides.
So, let's talk about what we just did.
We took an existing iPhone app and upgraded it to be universal and in doing so we took advantage of new technologies that are available and tailored for the iPad and even though we added all of this iPad-specific support, we continued to keep our app functioning just as it had before on the iPhone.
So now we're going to talk about adopting features from iOS 4 and to do that I'd like to call on to stage one of my colleagues, Barry Langdon-Lassagne.
[ Applause ]
Barry Langdon-Lassagne: Thank you Rob.
So adopting iOS 4 features.
Before I talk about adopting iOS 4 features I want to talk a little bit about maintaining compatibility because if you think about what we're about to do we're about to add new features to a Universal App but only to one side of that Universal App.
The iPad doesn't have iOS 4 yet.
So, let me talk about maintaining compatibility.
The first thing is in Xcode in your project info there are a couple fields I want to point out here in the Main Build Info and in the Secondary Window you'll see two fields.
One is iPhone OS Deployment target and that is, you can think of that as being the earliest version of the iOS that you want to support.
In our case it's 3.2 the iPad OS and then we have Base SDK for All versions and that is the build, that's the version of the SDK you're going to be building against so it's the latest and greatest.
You can think of it as the newest because it is the newest at the moment version that you're going to support but you can support anything from there and beyond.
So in a way you can think of that as the range of OS versions that you're supporting and in fact in Xcode 4 you could see this as a slider, graphically as a slider where you can choose the range which is a very nice presentation.
OK. Now I want to talk about some of those runtime checks that Rob described and they're sort of loosely broken up into three categories here.
The first one [UIDevice userInterfaceIdiom] is one that Rob showed in the demo and that's just the big switch am I on an iPhone or am I on an iPad.
I have lots of screen real estate or am I more constrained and so you'd use that any time you want to do something specific like in Rob's, in the case of Rob's Demo he was changing the behavior of the TableViewController to use a SplitView.
The next two NSClassFromString() and [NSObject responds ToSelector:] are much more specific.
NSClassFromString() will give you a reference to a class if it exists.
So you do this at runtime to check in your shared code whether a class exists so that you can use the class.
All of these things are things that you would want to do in shared code.
If you've already factored your code between iPad and iPhone generally you won't need to do runtime checks.
So NSClassFromString() is one that I will demo when I start adding iOS 4 features.
The other one, [NSObject responds ToSelector:] is the most sort of the most precise runtime check you can do.
This is checking if an object actually has a method that you're interested in.
So if you've, if you know the iOS has been upgraded, more API is available, what do we have 1500 new API with iOS 4 so NSObject you're going to have more functionality and you can check for the specific functionality that you care about at runtime and then take advantage of it if it's there.
The last one [UIDevice systemVersion], this is checking to see what version of the OS you're running on.
Generally you probably won't need this but we actually ran into a case in our Demo where we needed it and the particular case that we needed is we're going to be accessing a class that used to exist.
It existed in previous versions of the iOS but it was private and as of iOS 4 it's now public so we're going to need to check to see if we're on iOS 4 before we try to use that particular class.
So those are some runtime checks that you'll use in your shared code and for maintaining compatibility there are new frameworks with new versions of the OS and when you bring in new frameworks in a Universal App and you want to support versions that don't have a particular framework, in this case the iAd framework, you're going to want to weak link those against your applications so that they don't load on versions where that framework does not exist.
So the four features that I want to talk about today and I'm going to be integrating these into the Universal App that you saw Rob building.
First one is iAd.
So iAd is just a view.
Go to Interface Builder.
Now you have a new type of view called Ad Banner View.
Drag and drop it into your interface.
Wire it up in Interface Builder just like you do any other view and the things that I do with iAd you can do, you'll do pretty much exactly the same thing no matter what view you're adding.
For instance if you wanted to add MapKit or some other view that comes out in a newer version, Drag and drop.
You wire it up and you'll want to handle in code the case of rotating the phone so that it gets the notification that it's been rotated and I'll show you that in the demo and we've weak linked the framework because it's a new framework and it doesn't exist on iPad so we'll want to continue to work on iPad.
So, the next iOS 4 feature I want to add is something called UINib.
UINib is a class in UIKit.
It's a class whose purpose in life is optimizing the performance of frequently used NIBs.
So anytime you're using the same NIB over and over again in your application, you can get a performance boost from using UINib.
The classic example of this is a TableView where you're using a TableView cell over and over on your screen and when we did testing we found that you could load twice as many NIBs in the same amount of time using UINib as you could using the older method.
Third thing that I'm going to talk about is IBOutletCollections.
IBOutletCollections are a new type of Outlet that Interface Builder can use to connect multiple user interface elements at the same time.
So here you see an example of connecting up an array of TextViews to all of the TextViews in our interface.
You can use it to refer to multiple elements simultaneously that generally you'd do it for the same type of element but you can also connect to multiple types of elements at the same time.
So I'm going to talk about the syntax of IBOutletCollection a little bit.
Here's an IBOutlet.
You should all be familiar with this if you've used Interface Builder for the last what fifteen years?
Here we have a UILabel instance variable.
It's identified as an IBOutlet, which means in Interface Builder it'll go oh IBOutlet.
I bet you want to wire that up to a label and it will let you connect things to connect the label to labels.
IBOutletCollection is very familiar, is very similar.
Only the type is specified in parentheses so here we have an NSArray of labels and we're telling Interface Builder we just want to connect this to UILabels.
So if you wanted to connect it to different types of elements, you'd specify id in parenthesis or leave the parenthesis blank.
The fourth and last feature of iOS 4 I'd like to talk about briefly today is UI Automation testing.
This is a new feature in iOS 4 that allows you to automate testing of your applications.
So for example if you want to do performance testing you could write a script that goes through your app, does different things and you could put in some performance instrumentation in instruments and run it over and over again and as you modify your application you can be seeing the performance improvements and you know you have confidence that you're doing the exact same thing every time because you're using a script to do that rather than a hand going through each of the steps and UI Automation uses accessibility so it uses the built-in accessibility that's already there for voiceover support on the phone and the iPad and last year with Interface Builder we added support for setting the traits in accessibility directly in Interface Builder and I'll be showing that in the Demo as well.
So those are the things I want to show you.
I'm going to switch over to the demo machine and make them work.
So here we have the project just as Rob left it and this probably don't need to be running.
So let's add iAd Support first and I think now I'm going to close up these iPad folders because I'm only going to be working on the iPhone side of things.
It's really nice that Rob segmented this up between iPhone and iPad so it's easier to find stuff.
I'll do some stuff in shared classes.
I'll do some stuff in the iPhone classes and in the iPhone resources.
So let's Open up the RecipeDetailView here.
It's the iPhone version and I'm going to put an iAd here so I need to make a little bit of space in my interface.
Just going to move these text fields up and here's iAd in the Library and I'm just going to drag and drop it into my interface.
I want to pin it to the bottom so just going to unhook it from the top and stick, pin it to the bottom.
We'll Save that and then back to the project iAd is a new framework.
It's not available on the iPad so I'm going to add iAd here to the target.
So if I Double-click on the target for the Recipes application you'll see down here is a list of all the linked libraries, all the frameworks that we're adding.
Just going to add iAd here.
I will add iAd.
It's a little bit redundant.
Click here and because it's only available in iOS 4 I'm going to weak link this so that the iPad side of the application continues to build and run.
So now let's Build and Run.
So there's our recipes.
I'm going to Click on BLT here and you'll see the iAd, it'll test advertisement appears at the bottom.
I can click on it.
It takes over the screen.
You're still in your app.
You can close it.
You're still there.
Works great unless you rotate the phone.
If I rotate the phone you'll see the iAd didn't get the message that the phone rotated.
The view doesn't know we're in a new layout.
So I'm going to do some work in code here to tell it what just what happened.
Alright. So, this is in the DetailViewController.
I'm going to go to the header file here.
This is an iPhone only class so I don't have to worry about runtime checks here.
I can put in things that are specific to iOS 4 so I'm going to import the iAd headers and I want to add a new Outlet so that I can refer to this Ad Banner View from within code and I need an instance variable.
Save that and I want to synthesize.
Great and do my housekeeping.
OK. So now I need to tell it that it's going to rotate.
Well if you've already rotated the phone and you click and you tap a TableViewCell it's going to go to your DetailView and it'll execute the configure view method that Rob showed you if you have an iPad version of this on the iPad source file.
I'm going to add here.
Well ifSame [assumed spelling] just checks to see what's my orientation.
If I'm landscape I want to use the 480x32 version and if I'm portrait I want to use the 320x50 version.
So that takes care of one of the two cases that you have to take care of and that's the case where you're already rotated and you move into the DetailView.
The other case is what if you're looking at it in portrait mode and you turn the phone sideways.
Well in that case, in that case you want to get notified that the phone rotated.
Your view will get can be notified using this particular method will rotate to interface orientation and in this case we're going to get the notification that we rotated and here we'll call ConfigureView.
So let me, one more step.
I need to go back to my ZIB and I need to Connect up that Outlet that I created in the header file.
So if I Control-click on File's Owner you'll see there's an Outlet here bannerView.
I'm just going to drag connect that up to bannerView and now when I Build and Run you should see there's the advertisement and then when I rotate the phone, you can see it adjusts to the new orientation of the phone.
If I'm already in the RecipeView and I want some tomatoes you can see it knows that I'm rotated and it shows the proper orientation.
So that's adding iAd support to our Universal Application and handling the case of rotation and again as I said this will be true for any kind of view that you're going to add to your iPhone app so this stuff is pretty general useful.
Alright. So that's iAd.
Now let's do a little bit of performance optimization.
Let's use this new UINib class to make our TableViewCells load a little bit faster.
That's going to be in shared code.
So here we have the RecipeTableViewController.
This. Let me.
Don't need this space here.
This contains a method called CellforRowIndexPath [assumed spelling].
This is if you've ever used TableViews this is like the workhorse method for TableViews.
This is where all the stuff happens to load those individual TableViews and the particular piece we're going to focus on is this little if statement here.
Test to see if recipe cell is nil and loads it if it is nil.
So there's already been an optimization in the iPhone OS for a long time and that optimization is if you look at the interface of the TableView it's really only showing in our case about half a dozen cells at any given time.
So really we only need to load about half a dozen cells and as you scroll we can de-queue the ones we don't care about and queue up the ones that we do care about and reuse those.
So this code here that's been in here where we have a cell identifier and we're getting, we're de-queuing a reusable cell, it'll give us a new cell as we need them and it will de-queue and hold onto them.
It's a pretty good optimization but we can do better than that.
This loads 6 or 7 instances of RecipeCell.
We can make it so that it only has to load it once and we'll do that using UINib.
So, I need an instance variable so I'm going to just stick one here called RecipeNIB and I'm making it id just to be careful because we're in share code here and I don't want to do anything that's specific to iOS 4 without a runtime check.
So if I make this ID it's a little bit easier and then I want to create an accessor for that RecipeNIB.
Oops, got to get my housekeeping.
Put that down here.
I'm going to create an accessor for the RecipeNIB and this is where the runtime checks are.
First runtime check is that one I told you you're not going to need very often and that is getting the system version.
So why do we need to get the system version here?
Well it turns out UINib actually existed in previous versions of the iPhone OS, the iOS, only it was private.
It had different behavior so now that there's a public UINib we have to check to see if we're on 4.0 or later before we attempt to use it.
So, we're testing to see if we're on 4.0.
If we are then we're going to do NSClassFromString() which is another one of the runtime useful runtime functions that gets us an instance of that class, sorry, gives us reference to that class and then we can use it.
So here we're initializing RecipeNIB using this NIB with NIB name method to get this TableViewCell.
It's going to load it once and then once we've loaded it, you'll see this code actually just falls through the IF because the RecipeNIB will exist and it'll just return it every time you ask for it.
So it never loads it again.
It just does the instantiation.
Yeah, it just does the instantiation which we'll do down here in self-erode in its path in this little IF area that I was telling you was so important.
So let me make a little space here and I'm going to paste in an IF.
If I can get that RecipeNIB that means I'm on 4.0 so I can use it.
I can instantiate it.
If I can't get it I must be on 3.2 so I'm going to do the same thing I've been doing.
So I'm just going to cut this line of code out that I had here before and paste it in where I want it to run on 3.2.
So now I'm going to Build and Run and what you'll see when you run this on the device is zippier scrolling and that's exactly what you want.
That's what your users want.
Nice, smooth scrolling.
You really can't tell on the simulator because the performance characteristics are different.
It's actually much faster here running in the simulator.
So that's UINib.
Performance optimization inside shared code with runtime checks.
Next up IBOutletCollection.
Let's take a look at that DetailView that I've been working on.
I added an iAd so I'm thinking I'm using this recipe app, I'm in the kitchen and the phone is sitting on the table and I'm over here with the blender and the mixer and I want to see the recipe.
It'd be kind of nice if I can change the font size really easily so I'm going to add a slider in here because I'm going to want to change the font size of these TextViews.
I think don't need to make it that much smaller.
Let's throw a slider in here and I'm going to give it some values that make sense for fonts.
I'll start at 8 point and maybe go to 24 point and then I think the default should be right in the middle, 16.
so I've got a slider and I've got some TextViews and I could create a separate Outlet for every TextView here and wire it all up in Interface Builder you know the way we've been doing it all along but I'm going to use an IBOutletCollection.
You can imagine if you have a more complicated layout IBOutletCollections will be more useful because you'll have more things that you'll want to wire up simultaneously.
We've only got two here.
So, we're in the TableView sorry the DetailViewController so let me switch on over to the header for the DetailViewController and here I'm going to add the IBOutletCollection.
So you can see I'm telling it that I want an array of TextViews.
I'm specifying UITextView because that's what I want to connect to and I need an instance variable and I need to do my housekeeping.
Great and now I need an IBAction to use for that slider so let me put an IBAction right here.
It's fairly simple.
This IBAction is going to be connected to a slider and what it's going to do is it's going to get the value from the slider.
I set it between 8 and 24, nice font sizes and it's going to use set value for key on the TextViewsArray and just set the font size for everything it finds.
Alright. Let me go to the Header.
I want to specify my IBAction here so the Interface Builder sees it.
Switch back to Interface Builder and you'll see over here.
Let me just bring that up again.
This is the highlight for the File's Owner.
You'll see OutletCollections has just shown up so this is a new section that shows up and it shows your OutletCollections.
This one is the TextView one and you can see as I drag over to my interface only the TextViews are going to highlight.
The image view doesn't highlight.
The slider doesn't highlight so I just wire this up, all of the elements and here you can see all of the elements that it's wired to.
It's very, very nicely presented and then got my slider and I want my slider to run that IBAction that I created so I'm going to Control drag from the slider onto File's Owner.
It'll bring me a list of all my IBActions.
Click on Change font size.
Save. Now Build and Run, and they'll click on BLT and drag and you can see it's changing the font size for all the TextViews simultaneously.
So that's IBOutletCollection.
[ Applause ]
So the last thing I want to show you in the Demo is UI Automation Testing.
You should be thinking about testing whenever you're doing development and I just added a new control to my application.
So I probably want to test it and hey I added the new control and I probably haven't made that accessible yet so I should do that at the same time.
This is great.
I get two things for one.
Alright. So we'll go back to the interface.
Oh that's not the right one.
Go back to here and my slider is already selected so I'm just going to Click over here on the Identity inspector and here's that accessibility section I was telling you we added a year ago to Interface Builder.
So this is where you set the Label and the hints for accessibility and I'm going to just give this slider a nice human readable title.
I'm going to call it Slider for Font Size.
So not only will that be the thing that says when you run voiceover in your app, it's also the unique identifier to refer to that slider in your test code.
So if you have multiple sliders you'd probably want to give them names that made sense to people using voiceover and those names because they're unique will also be great for referring to in your automated testing because they'll keep working even if you rearrange your interface.
You're not referring to the x y coordinates of things.
You're not clicking blindly.
You're actually looking where you're clicking.
You're clicking on things that you know the name of.
OK. So I've given it a name.
I've given it a Label and I'm just going to rebuild the project to make sure everything's up to date.
Now I'm going to switch to instruments.
In Instruments you'll see there's a new template for automation so I'm going to choose that and since I've already run this once on this machine, let me just hide stuff in the background.
I'm not going to take time to show you guys how to write tests here.
There's actually another session for writing UI Automation testing that covers this in much, much more detail than I would have time to cover here.
The important thing that I did to this Quick Test is I referred to that slider by the same name that's in accessibility but you can just take my word for it and then I want to choose the process that I'm running against.
In this case I'm going to choose recipes app in the simulator.
You can also run against the phone or eventually against the iPad and then I'm just going to hit Record.
So now I'm running instruments and you can imagine I might be putting other instrumentation in here.
I might want to check for leaks.
I might want to do performance inspection.
I might want to look for over-releases.
Whatever you want to do in Instruments you can do at the same time as you're running an automated script and you can see it's dragging that slider for every single TableViewCell in my application.
It's just going to keep on going through until it gets to the bottom and you know I could have looped it so it could have just kept on going for a long time and I'm logging a little bit of data here in Instruments and now I can go take a break and I'm still doing work.
I love it.
[ Applause ]
We've got too many recipes.
I was going to let it get to the end but it will say pass when it gets to the end.
Let me switch back to slides.
OK. So what I just showed you I added iAd support to our Universal Application.
Weak linking the framework so that it'll continue to work on the iPad.
I added UINib for performance optimization to share code during runtime checks to make sure they behaved correctly on both the iPad and the iPhone.
I used the new IBOutletCollections to connect multiple user interface elements at the same time and change them at the same time and I gave you a little peek of UI Automation testing so that you can be testing your applications while you're off taking a drinking break here.
OK and that's everything we wanted to cover today.
So, Rob took our existing iPhone application, he made it universal.
He factored it into two pieces.
We then took the iPhone side of that Universal app and we added the iPhone iOS 4 features to the application all the while keeping an eye on maintaining compatibility in our app.
So now it's your turn.
Take the things you've learned here, apply them to your applications, your development process and make some great applications.
Thank you very much.
[ Applause ]
There are some related session's tomorrow afternoon; Interface Builder in Xcode 4 is something you really should catch.
Xcode 4 and Interface Builder.
Xcode and Interface Builder have been reunited and or have been united and seeing how you can use Interface Builder in Xcode is really amazing.
Also the Automating UI Interface session actually is happening at the same time as this session so you'll want to catch that on video and Integrating iAds there's a repeat next or this Friday morning so you should take a look at those sessions.
Here are a couple more resources that you might be interested in.
Thank you very much.
[ Applause ]