WWDC2010 Session 305

Transcript

>> 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.
Really simple.
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.
Excellent.
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.
Save that.
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.
Rotate it.
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.
Barry?
[ 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.
It's based on JavaScript which probably many
of you already know and it runs in instruments
which means you can be instrumenting
your code at the same time
as you're stressing your code,
as you're exercising your code.
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.
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.
Excellent.
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.
Very simple.
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.
Since I've already run it, it's actually already
referring to my Quick Test JavaScript that I wrote.
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 ]