Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2009-105
$eventId
ID of event: wwdc2009
$eventContentId
ID of session without event part: 105
$eventShortId
Shortened ID of event: wwdc09
$year
Year of session: 2009
$extension
Extension of original filename: m4v
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2009] [Session 105] Cut, Copy, ...

WWDC09 • Session 105

Cut, Copy, and Paste on iPhone

iPhone • 49:49

Cut, copy, and paste is a major new feature of iPhone OS 3.0. Learn how it can enrich your applications and how best to take advantage of it. Find out how to enable cut, copy, and paste in your custom controls, add undo support to your application, and use the system pasteboard to share data with other iPhone applications.

Speaker: Andrew Platzer

Unlisted on Apple Developer site

Downloads from Apple

SD Video (82.6 MB)

Transcript

This transcript has potential transcription errors. We are working on an improved version.

Good afternoon, my name is Andrew Platzer. I'm a Senior Engineer on the UIKit Framework, Cocoa Touch, and today I'll be talking about cut, copy, paste on the iPhone. It's a bit more than just cut, copy, paste. We'll be talking about editing and sharing your data on the phone. This involves presenting the edit control so these are canned cut, copy, paste to specify which actions are available so maybe sometimes they can cut, sometimes they can't.

We'll talk about the pasteboard and how to share your data with, between your application and other applications. And finally, we'll talk about the undo manager. How to add the undo to your application so the user can easily correct their mistakes. For this, we'll talk about four new features. We'll talk about the menu controller.

This is a singled instance, sense of a class that you can use to present the cut, copy, paste menu. We'll talk about the responder chain. This was already partially used in previous releases, but now it's got more importance with the menu controller. We'll talk about the new Pasteboard API and then we'll finally talk about the addition of the undo manager. For those of you who use Mac OS X, this is the exact same undo manager.

So first with the menu controller. The menu controller presents a way of access to presenting the menu, a cut, copy, paste menu. It allows you to, it presents a sort of single shared instance so that you don't need to create your own or you don't need to worry about the window or anything like that. You just talk to it. It lets you position and show or hide the control as necessary depending on what the user actions are. And finally, it lets you specify which items are enabled.

So only cut or only copy or only select for example. To show the menu, you just want to use one call. Set menu visible animated. You'll just say set menu yes animated yes and then the menu will fade in and you'll want to trigger the certain user action. If you look at for example in text view, it's a press and hold gesture. And you'll want to try to do something similar so the user knows they can do it at a certain time.

To position a menu, you'll call on a function called set target erect and view. And you pass in the target rectangle. This is sort of the area where there's a selection or it might just be the view bounds. This target rectangle can be zero width or height. So it can just be a point specifying the origin or it can be like a vertical insertion mark or something. And the menu will be automatically positioned above or below that bounds so it's always on screen.

One thing to notice that the target rectangle you pass in, though it's in a view, it's actually converted to screen coordinates. So you don't, it doesn't actually track the change in the view location. If the view scrolls programmatically, you're going to have to call this function again, even while the, the controller is visible in order to position it into the new location. And so here's an example of how we might show the menu controller and show the menu.

Here's an example, we will press and hold so we'll just check to see that touch has began. Time, we remember, is now, enough time has passed. We'll show the, we want to show the menu. We'll ask for the shared menu controller. You don't need to create it, just ask for it.

Then we'll just set the target [inaudible]. In this case, it will be the views bounds. So anywhere around that view above or below it will show that we're possible. And finally, we just say set menu visible yes, animated yes, and the menu will fade in. In order to hide the menu you could just do the reverse.

It's automatically hidden for you so when these are touches in another view, or when an alert comes up, an action sheet comes up, or when a system alert comes up like you've got a phone call and the app deactivates, we'll automatically hide it for you so you don't need to do anything.

It'll also hide when you actually select the command. You can hide explicitly using set many visible animated or just set the [inaudible] property, which will instantly hide. For example, view is instantly removed, you'll want to also take away the controller right away instead of waiting to fade it out because your interface no longer is showing.

It needs to show it. And if you have any inter, any feedback or some kind of you know, selection rectangle or whatever that depends on the menu controller, the menu controller's menu being visible. You'll want to listen for UI menu controller will hide menu notification. And this is sent whenever that menu bar is hidden and you can change your interface as necessary.

If you don't care, then you don't need to listen to it and it'll just go away. The menu interface is automatically localized, so depending on the language the user has chosen, they will get the appropriate text and the items for that language. But, it is not customizable. This means that the item names are fixed. You cannot change cut to something else and you cannot change select all of something else or whatever.

Currently, you cannot add any items. That's it, you've got cut, copy, paste, select, select all, you can't add any more. And finally, please don't even try, I know some of you like to...

[ Laughter ]

...push the boundaries. But you will break it in the future, I assure you.

We've already seen this in other cases, so please don't. Now that I've shown you how you can present the menu, how you can show it, I want to talk a little bit about responders because this is used to determine which actions are actually available, which actions will be presented to the users when the menu control shows up. In previous releases, we did have responders, you may have seen UI responder, but they weren't really used.

They were used for a couple of things only. Just touches, when you touched in a view, you might not want to handle it, so you'd want to let it go up the view chain and this is actually done going up the responder chain, which for view, this is parallel to the view hierarchy. There is a UI application property called key window, but it wasn't really used.

But you may have seen calls in the sample code or your code that calls make visible in Key. And UI text fields and text views would become first responder, and I will talk about that, but no one really cares. So for example here in 2.0, 2.21, whatever you! know, sees or touches in a view and that just gets called up the responder chain, the view chain to the next view.

Or maybe the text field becomes first responder, that was about it. And three, the responder chain is now much more important. The UI window has a key window property, read only, which will tell you if it's key window. You do need to call, make visible in key if you want to make sure you're the key window as far as the application is concerned.

And this responder chain is now used for what we call menu item validation or just to indicate, Yes this action is available, or No, it's not. So if you're going to use the menu, you're going to have to be a bit more key window and responder aware so that you're going to have to know when you're the first responder and when you're not. Or if you're using multiple windows, when you're the key window or when you're not. For most of you, you know, you'll have the one window and your interface is automatically key window if you've called make visible in key.

You don't need to do anything at least as far as the key window is concerned. So for example, here, one additional thing, we now have, I was going to say, we have view controllers, they're actually part of the responder chain. So even though they are not part of the view hierarchy, they actually do participate in the responder chain. And by default, the view controller's next responder is its view's super view.

So both the view and the view controller are associated-- would point to that super view. So now, when you click on a text field it becomes first responder, we really are interested in a bit more information than we were before. The application is a key window, it'll point to that particular key window.

That won't change when you click in that text field so you'll need to set it explicitly. And then the text field of course will become what's called a first responder. On the other hand, maybe at some point programmatically, you want the view controller to be a first responder. And I'll explain why you want to do that.

And in this case, now we have a direct link from the window to that first responder. And then we can follow the link backwards all the way down, all the way up the responder chain to the application. I was going to say, see here UI window's next responder is actually just the application and that's the root.

UI application's next responder is nil. So in order to be in the responder chain, you have to be subclass of UI responder, UI view, UI view controller and UI application are subclasses of next, UI responder, which is sort of the base class for most of the interface classes.

These are chains, as I said, using the next responder. That's a read only property by default views, next responder is its super view and so view controllers, this is views, super view and so on. You can override that by subclassing and returning something different. But in general, you probably don't want to.

If you wanted to have a custom responder class inserted somewhere you could, not something you'd probably want to do often. And as I said before, each window has a first responder, so if you're going to switch windows, be aware that, you know, when the view is only the first responder in its window and each window will remember it even if it isn't the key window.

So to become first responder, you'll need to subclass one of the existing classes by default-- can become first responder returns No, and in order to become first responder you'll have to return Yes. Once you do return yes for this, whenever you want to become first responder just call the function to become first responder.

That will over, go up the responder chain and tell the window you are now the first responder. If you have a need to kind of feedback to show like a selection or that you're the active editing area, that you are as the first responder, you'll want to override to become first responder or resign first responder in order to display that in, to display that interface.

If you just want to become first responder and have no visual, you don't need to do anything as long as can become first responder returns yes, the window will know you're the first responder. And one thing to really note is your first responder must be in the window hierarchy because, so the only one that knows about the first responder is the window. So if you call it too early at the wrong time, it'll just be ignored.

And I'll show you where you would call that for example the view or the view controller. So here's a sample of how a custom subview, a custom class subview which returns can become first responder will, will become first responder. And again, here, we just see if the touch ended inside the view itself and touches did end.

The touch ended and just called self first responder. So at this point you know you're getting the touches, it means you're part of the view hierarchy, you can do this. For view controllers this is even simpler, just in view did appear call become first responder. At this point in view did appear, the view is now part of the hierarchy, you'll tell the window I'm now the first responder.

Don't do it in view did load, view will load, the view will appear because at these times your view, which your controller points to will not be part of the window and your becomes first responder will become a new operation. So if your custom view or custom view controller does become a first responder, you'll want, you'll want to be aware of when that happens.

Text field and text view automatically become first responder on touches. So if you, if you set your own view to be first responder but you've got a text field inside there, and the user clicks on it they will become first responder. You want to listen then in that case for either text field or text view delegate notifications, the text fielded an editing call or the text fielded and editing notification. Those are your signal that you can now set a responder back to your view or to some other view or to some other view or whatever. We don't automatically save what was the previous first responder and then swap it back.

We just, you know, we'll just reset it to nil effectively. So right now, we provide some system editing actions. These are the only sort of public responder actions you need to worry about, cut, copy, paste, select and select all. These are declared in UI responder.h. Most classes don't implement them, yours will as necessary.

By default, if your first responder and the menu controller needs to decide which menu item is to show, or cut or copy or whatever, if all you do is implement them, they'll be enabled. They'll always be visible when the user shows the menu controller and you're the first responder and it checks to see. These standard actions which are declared in the UI responder.h are not actually implemented in them. They're only implemented in text field and text view.

So you'll need to implement the ones that you're interested in providing. If you want to customize which actions are available, so when the menu controller is shown, you only want to show the cut item but you also want copy and paste so that there used at other available to other times.

You'll want to implement canPerformActionWithSender. This is called a lot. Every time the menu control is shown for every single action this is called so you'll want to be quick about it. You don't want to sit there and do a lot of calculation. Should I be able to cut, should I be able to paste. Just check the state of the application, state of the view, whatever and return yes or no.

Any time you called with this with an unknown action, one you don't recognize, just pass it on to the super, super, the next responder, the UI next responder canPerformActionWithSender. That'll handle passing it down the chain. And if we have other actions in the future which you don't know about, you won't block them from the application presenting them.

So you always want to make sure that you don't, you know, don't just absorb the unknown actions but rather pass them on down. So here's an example of how pretty much a standard template you might use for implementing canPerformActionWithSender. Right now this view for example, or view controller, doesn't allow editing so it also turns no uncut. It will return copy if we have something to copy.

If we have some sort of selection, otherwise it will return no. Paste, because also we don't allow changing, we'll return no right now. And for select all, we'll say yes we can select something if there is something there to select. In this case, we'll just check maybe an item count variable or something like that. And as important, as I said, before, importantly you must call supers canPerformActionWithSender if you don't implement the action if you don't know about it, just pass it onto the next person to worry about.

And so now, I'd like to give a quick little demo to start. That shows how you would present the menu and how you would show certain items and certain times and how the responder chain works. It's a little application here and what it does is it presents Sumerian cuneiform glyphs [assumed spelling]. Sumerians invented writing about 4500 years ago, there're about 200-300 different glyphs written on clay tablets.

And so here we'll show you two rows of them and I've written a small view here that tracks clicks and will show an insertion point or will show a selection as you drag stuff out. Now what's more importantly, when you click and hold it long enough, you get a cut or copy menu.

Or if you've just got a selection, or sorry, insertion point, sorry, you'll get just a select and select all. So it's similar to UI text field depending on whether something is selected or whether there is a selection range, you'll show different items. Sorry for that. Now just for your other code, there's a sample application, which I believe is available, and this will, this, the important class for this one is UI tablet view controller, Sumerian tablet. We'll start with the responder stuff.

Here as I showed before, in view will appear, where'd it go, sorry. Oh view, sorry, view did appear, my apologies, we did call first responder. And what we've done here involved of course, overridden can become first responder to return, yes. So that's all we needed to do. Now we're part of the responder chain.

When our view, which slides in has been shown, we will say become first responder and because we say we can, we now are as far as the window is concerned. So now, we've added a function that the view calls back into the view controller. This is a view controller here that says the view did tabs. And now we know to present the view controller. And so at the bottom of the file here we have a show menu.

We get the shared menu controller and if it's not already visible, we set the target req to be the tablet view's selection req. So this is the function we've added on the view to return the bounds on just that selection rather than the bounds of the whole view.

This way we won't have the menu controller presented in the wrong location and we want it faded in. So that's all we needed to do to present the menu controller. Now we have to indicate which items to present. So we've implemented as you can see, cut and copy and paste and select and select all.

So all five of those known public actions. And we also have implemented the, excuse me, they can perform action with sender call and as similar to the template that you already saw, we check based on the certain actions. For example, cut and copy if there is a selection range. Or paste or select or select all.

So for select or select all, we only want to show it if there is something to select, the count is greater than zero and there is no current selection. So if there is a selection, we only show them cut, copy or paste. And as I said, very importantly, we call super canPerformActionWithSender in order to present to-- in order to pass on actions that we don't understand.

So now, I'd like to talk about the pasteboard. So you've got cut, copy and paste, you've got a selection, these are cut, you find the text that's been cut and so on, but you've got to put it somewhere and you want to-- you could put it just in your own application, you know, and share it within there but, you know, you probably want to actually put that data, you know, on somewhere where other applications can read it. So if these are paste texts or something they can put the text somewhere, another application can read it. Or maybe if they put text on the pasteboard, you could read it.

It gives you a shared and persistent storage. So even though your app quits the, the text remains. It allows you to set single or multiple items so you can set a single string or you can set three strings or a mixture of three strings and three images of whatever you want to do.

And then for each item you can specify a unique representation so you can have a plain text item that also has rich text. And then depending on the app that's reading that information, it can decide, Oh I understand rich text or I only understand plain text and I'm not going to be able to give you anything fancy. And we provide a standard general pasteboard which every application knows about and which should be the most common thing you need to use. In order to get a pasteboard just ask the UI pasteboard class for the general pasteboard.

That's the most common paste you'll need. That's the one every application uses. If you want to do your own pasteboard stuff either internally when you app or maybe a suite of apps, you can create your own pasteboard. You can save pasteboard with name, create and that lets you specify a name.

And then ask to create it if it doesn't exist, or if you say No and to create and it doesn't exist, you'll get nil back. And you should name the pasteboard something reasonably unique like com-- YourCompany.YourProgram. You can also ask for a pasteboard with a completely unique name that gives you a long, you know, unique id string that used-- that only you know about.

You might want to use this for an internal pasteboard or something like that or if you somehow make it available and known to other applications, they can use it. The pasteboards that you create, you own. So your application creates that pasteboard, it's marked with being owned by you. Other applications that are able to read and write to just that pasteboard will not be able to change attributes about that pasteboard and I'll talk about some of those. So now about pasteboard items.

You want to put stuff on the pasteboard, you want to get stuff off. You can just provide a single item and we have some simpler API for doing that; I'll show you. And for example, here we have a single item with a plain text string. As I said before, you can have unique representations for each item. So you can't have two text values for the same item, but you can have a plain text value, a rich text value, you could even have a image representation of that data or a PDF representation of that data.

And then we allow multiple items. So for example if you're copying from a webpage, which is a mixture of text and images all in a steam there, you'll want to, you know, copy a block of text, copy a picture and so on and put them one after the other. Not every application will be able to read all the items, but at least they'll be able to, if they can, present a much better representation of the data that you copied.

The data itself is normally just passed using NS data. There's the base sort of getter for this is data for pasteboard type, you pass in a single type, you get back a chunk of NS data. That's up to you to interpret. We also provide a property list version of that for you know, pretty much standard foundation property lists as any combination of dictionaries, arrays, strings, dates, numbers and the data itself and you can mix and match that.

That gets converted to an NS data when it's saved and then reread back out. You can't extend these types right now as far as I know, that's it. So you'll have to, if you've got a custom type of data you want to store, you'll have to convert it to an NS data to put in a property list.

The pasteboard contents we'll talk about. Users put the pasteboard data on, maybe a plain text or whatever, you want to know what's there. The simplest call list contains pasteboard types. You pass in an array of types and it returns Yes or No, whether it's available. If for some reason your application is interested in maybe preferring one type over another, if it's available you can ask for a complete list of types for that item. And this pasteboard type returns an NS array of strings, the pasteboard types for the, that particular item.

So for example, plain text or rich text. And here is the single item method. These are the most common cases you'll probably want to use. They all operate on the first item only. So if you've got multiple items, these only work on the first in terms of reading them and if you set to them using these methods, you will clear out all the other items.

So you say for example, set data for pasteboard type and there's five items, you only have one item now left on the pasteboard, it'll clear out all the other ones. And there's basically is the ones I've talked about. The NS data setter and getter and the property list setter and getter. We do support multiple items as I said, we add one more property to the pasteboard, which is number of items. We use an index set to specify which items you're interested in. If you're interested in items 1, 3 and 5 for example, you can specify that.

Or you can just pass it, you're interested in all items. And there are equivalent methods for every getter and setter that you saw. And contains some property lists and so on that take this NS index and set it. So for example, data for pasteboard type returns a single NS data, data for pasteboard type in item set returns an NS array. This returned list is compacted. So if you only ask for items 1, 3 and 5, you'll get an array of threads and back.

So you'll have to do that mapping if you're really interested. And we do provide a bit more sort of basic direct access to all the pasteboard items. There's a read write property that takes an array of dictionaries. You can set and get as necessary. Be warned of course, that this is going to get you everything in the pasteboard, which might be a lot if someone's pasted a few pictures.

As a bit of a convenience, you can add items one at a time. Each of those items will be a dictionary of pasteboard type and then the raw NS data. We don't do any conversion to property list types or whatever so you're going to have to provide us just the plain NS data along with the key which is the pasteboard type.

So as I said before, if you create your own pasteboard, it's not persistent by default, but if you own it, you can set, you-- I'm sorry, if you don't mark it as persistent, it will be moved when the app exits. Which might be useful in some cases, but probably you'll want to keep it around. So if you're the owner, you can set the persistence flag on the pasteboard. But as I said, you, it's not a hundred percent persistent. There'll be cases where we will erase it.

Maybe the application that owned it went away or maybe we ran out of space or we rebooted or so on. So don't use it as a general storage mechanism. It's not guaranteed to be around as much as, you know, just writing a file into your indicative space. And as the owner, besides hitting the persistence blog, you can call it remove pasteboard with name. You don't need to create, load the pasteboard note to remove it, just pass in the name. So you need to know the name and you need to own it in order to remove it.

If you don't it just won't do anything. We do provide support for a few pasteboard types. Sort of predesigned easy setter or getter methods so you don't need to ask for the pasteboard type and convert to an NS data or so on. Right now, we support strings, images, URLs and colors.

So that's NS string, UI image, NS URL and UI color. We have a single item case for this for example here we're asked for the general pasteboard. We set the string value to be [inaudible] and that replaces all the other items. We have multiple item versions of these calls. So for example, a string's property, which you can set and get which works with an NS array like you saw and that will put in or get out an array of information.

Obviously if there is only one item, you get an array with one item back. And just like the other case with the direct setters with the single item, if you use the single setter like just setting the single string value, you will erase the old-- all the other items if there are multiple items there. Just something to be aware of.

And one other proviso is that UI images are automatically converted to PNGs to be stored. This is a lostlist [assumed spelling] format and we don't keep track of what the original data was. We don't know if it came from a JPEG or a TIF or whatever you provided. So it will be slow if it's a big image. So if you've got a, you know, three megapixel image and you're converting from a you know, UI image to stores of PNG the user will notice that's going to take some time.

So if possible, you'll want to keep track of the original data that this came from. [Inaudible]. So if it's originally from a JPEG just keep the original NS data that stores that JPEG around, put that on the pasteboard rather than using the UI image calls or setting the image or images property.

For pasteboard types, we use what are called UTIs, these are universal type identifiers. This is something that's been on Mac OS X already and just presents sort of a simple not quite inheritance tree but something similar to different known types. For example, public.PNG with just the PNG type is this type of public.image and so on.

So you want to use these types. Why? Because it'll give everybody else knowledge about what you're putting on the pasteboard. If you put, you know, your company.type on there, only apps that have specifically written to understand your company's type, we'll know what to do with the data. Otherwise, it will just ignore it.

So for example, here we've got some of the UTI types defined, PNG, mp3, URL and so on. And obviously we don't use them all ourselves, but they are all available and you should use them if you have your particular type on the pasteboard. We use the, for example, when we provide the list of types that we understand.

So for example the image type, we actually say we can understand these following four image types in this order actually; PNG, TIF, JPEG and JIF. So any of those items are placed on the pasteboard and you ask for the images for that item, you will get one of these back.

It will read one of these and you'll get an actual image back. This is the same API as Mac OS X. A couple things we don't support are things like four character codes; we don't need them. And the only difference between us and Mac OS X otherwise is that this is now located in a new framework called mobileCoreServices.framework. So you'll want to look there for this API or for any of the types you might want to put on the pasteboard that we already defined for you.

A few sort of dos and don'ts just to reiterate what I talked about earlier, do add multiple representations if possible. You know, provide a plain text at least or a simple PNG plus, you know, more complex data type if necessary for example maybe a raw or camera or whatever or html text or something. This way even a very simple application will be guaranteed to be able to paste or if they want to paste text or some plain text.

As I said before, just to read right. Don't use it as a general storage mechanism, we'll erase it at various times you, you know, your user will be annoyed it something goes away. And use the original image data, don't let it uncompress and then recompress as necessary, that's just going to annoy the user. And just don't put secure data on the general pasteboard.

Just a good rule you know if someone pastes a, copies of a password, you don't want to put it on the pasteboard. Otherwise of course, some-anybody-- other application comes along and will be able to read it. So, for us for example for secure text fields, you cannot copy the text into a-- into the pasteboard. We just disable the copy command completely. So now that I've sort of talked about the API, I'll show you how we've added that to the application I showed you earlier to allow you to cut and paste between the application and another application.

So back to here. There are only three places we need to add to give us full pasteboard support. One is in the paste command, and as I said before for example, we get to the general pasteboard, and we ask if it contains the pasteboard type and we'll just ask for only a plain string. So we're not a smart app, we don't know images and rich text.

We'll just look for plain text. Then there are only two operations that really involve a pasteboard. That's cut and paste. Copy is really, oh sorry, copy and paste rather. Copy will read the text and put it on the pasteboard. Paste will take the text from the pasteboard and put it into your document. So for copy, we ask the tablet for the text in the current selection range and copy will only be enabled if there is a text selection so we don't need to check it here.

We'll ask, and then we'll just tell the general pasteboard that its new screen value is this text. In the reverse case, we've enabled paste if the pasteboard contains some kind of text data. So we'll ask for that text and then we'll call a function called swapTextAndRange will replace the existing range with this new pasteboard text. And I'll show that function later with, with-- as we go along. And that's all we really need to do.

Now we can cut and paste text and we can share it with applications. So we can for example, select these two characters. And what you see at the top is the transliteration of the glyphs. So the first glyph is Tom and the second one is Lou. So we will copy and then maybe we can paste it down here. And notice here now, paste appears. Before there was nothing in the pasteboard, so the paste command wasn't enabled.

Now it is because there is something and we paste and the text is there. And of course, now we can share this data with other applications. So I've got just a little application, all it is, is a text field, but because we have cut, copy, paste and text as well, what we've done is actually pasted in the transliteration text. And there you can see it's copied the text in there. And we can even edit it here, add maybe another character.

Copy that character. Now we go back to the original application, which knows how to convert from the transliteration back to a glyph. So now, we can say paste and we get a new character in that's the glyph that represents End. So that was all to add cut, copy, paste. Now your application is so much more useful because other applications can get at your data.

So now we've added-- show the cut, copy, paste menu and be able to share that data with the pasteboard, we'll actually be able to give the user a chance to maybe undo the accidental cut. They've cut or they've done a couple of cuts and pastes and they go, That's not what I wanted to do.

So the Mac O-- the Mac OS X foundation has the NS undo manager and we've just brought that completely over to the iPhone. So it's in the same location and foundation, it's the same API as Mac OS X, NS undo manager so if you've used that before this is no different. And I just want to cover some of the features for those that haven't used it before. What you need to do is every time the user does an action, a cut, a paste or something that modifies the document content, you want to register an undo action.

Something that you'll want to do to reverse what was done. The undo manager will keep track of all of this for you. It will keep track of a stack of actions as you keep doing more stuff, as the user keeps doing more stuff. It'll keep track of that stack and it'll be able to play it back in the reverse direction, so as the user does undo it goes back down and keep track of what was done and undone and lets you redo it again.

It allows you to group actions together so for example if user does a bunch of typing, it'll group that all under an undo typing block rather than having to undo every single character. And it keeps track of an action name for an interface so when the user sees, you know, that they need to undo and they bring up the undo alert, you will-- they will see undo action rather than just a plain word undo.

And this way it will be more likely to remember what it is they want to undo. So to create an undo action, you need to do one of two calls. And I'll show you why use one over the other. There's a target selector version that just takes two objects and then a method name that you want to actually perform as the undo. Or you can use what's called an invocation, which allows you more complex parameters to the undo.

So you'll use the target action case when you've got a simple reversible setter, like a setter method or you've got a case where the undo action will just take a single object, an NS object. For example here we see a set color method and that's completely reversible if you want to set the old color, you just need to set the old color with the-- and you need to remember what the old color is. So what we do is we get the undo manager and then we just call register, undo with action with target selector and the object that you're going to undo.

So for here it's a UI color and all we need to do is call setColor again. Call it with setColor with the original color under bar color and that's all. Undo manager is smart, it will remember what it's used as the undo color, it'll remember what you passed in and as it does undo and redo it will set the appropriate value. So you can just keep calling the same function over and over and over again to undo and redo.

For the invocation, we use this for if you've got multiple parameters and I'll show you an example in the demo. Or if you've got something that the parameters are not an NS object, something that can be retained. So for example here we've got a set level function that takes an integer so we can't retain it but instead we'll call it prepared with invocation with target self.

That returns an invocation object, something that will remember the action and pass in the function. And in this case this is a set level call is also reversible. So we can just call set level again. But as necessary, you can have a undo set level call that you would use instead.

And so you'll use that one instead of this set level call. So it's up to you and your design app and what kind of data you need to pass in. Once you've set the action, you'll want to set the undo title. So this is what shows up in the alert when it pops up and instead of just saying undo and redo, it'll show undo. Like here is setColor or redo setColor. So when the user shakes the phone, they'll see undo setColor, Oh yeah, that's what I did, I set the color.

Now that you've added all these actions up and down and there's a whole stack of actions, you'll want to manage the sort of undo state with actions that are available and when certain actions are no longer available. By default, you can register multiple, you know, actions at the same time. The user presses a button, you do three cuts or something or whatever, you can register three separate actions. They are grouped together for that single event as a single action. If necessary, you might want to combine actions across events.

So for example typing or maybe you've a graphic and you're moving the image around on the screen multiple times, the user probably wants to undo it right back to the original location rather than having to undo each step as you did it. In this case you can use beginUndoGrouping and endUndoGrouping and that will just sort of capture all the actions you did as one big group until you hit undo grouping. These are nestable if necessary so you can have your code, just call us multiple times.

You'll want to clear the undo stack at certain times. Sometimes when the state of the program changes you've like saved a document or whatever it is you've done, that you can no longer undo. So you'll just call the undo manager removeAllActions or if only a certain object is going away that's no longer undoable, you'll need to call rootActionsForTarget. And that target might be the view that registered the undo action or the controller or whatever. You have to do it based on the design of the program so it depends.

A lot of cases you'll just call to remove all actions to clear things out. To enable undo, you don't have to do anything. If you put something on the undo manager and told the undo manager here's something to do and the user shakes the phone they'll get the undo manager alert appearing, bouncing in and they'll be able to undo. So you don't need to do anything, it just shows up. You can turn it off temporarily; there's a UI application property that allows shakeToUndo.

If that's turned off, shake that [inaudible] if that's turned off then you won't get any alert appearing and the undo stack will remain as it is. It's invoked using the motion events. The UI responder if you've seen in 3.0 has the motion began and ended and so on and we use the exact same system in the responder chain in order to show that alert.

So even though you might have undo items on the-- items in the undo manager, you'll also still get motion stuff. If you do want the undo manager to show the alert, you'll need to make sure that those motion events get passed down or passed up the responder chain to the UI application. So if you intercept motion events and you're not interested in doing them yourself, you'll want to make sure you call super motionBegan, motionEnded and so on.

So we know that we can present the alert. If there are times when you think you want to shake to randomize or something, this is where you would use the flag applications for shake to edit to turn it off temporarily while they're randomizing that particular view, and then they go back and reenable it.

By default, we provide you an undo manager. That's in the UI window. So same as the first responder, there's a shared undo manager in the UI window. We follow up the responder chain so there, the UI responder itself has a method called undo manager and you just say self.undoManager, and that will actually do the search up the chain for you. By default it said, Every window has its own undo manager, so as you switch windows you'd get a new undo stack available. We do sometimes need special undo managers because of modal state or some other state.

In this case for example, text field. When you activate a text field, we want to allow you to edit and undo and edit. But then when you finish editing in that particular text field, you want to clear out, you don't want to have any more undo, you don't want to go to another text field activated undo what you did in another text field into this one.

And also the user might forget, or it's been a while, so we just-- and this is no different than Mac OS X, we just clear out the undo stack when the text field resigns first responder. Also, for those of you who use Core Data, there is an undo manager available in what is the, NS managed object context.

So if your class, if your view or controller is the first responder, it can-- and it works with Core Data-- it can just return that as managed object context undo manager. And then whenever you do any Core Data operations, adding or removing from the Core Data database, it'll record those undo actions so you'll be able to just say, you know, undo, you know, inserting that row or undoing setting that property and you'll get that automatically.

So you don't have to do anything there except return the undo manger that you want to use. And you might have your own cases where you want to have custom undo managers. So for example, in the demo application, which I don't have there, as I go to each different tablet, I might want to be able to undo there and not in another one.

I want to undo, sorry, in each tablet uniquely. So I make a change in one, I go to another one and make a change, go back to the original and then it'll be able to undo that original change in that original view. And so in that case, each view would have its own undo manager. And then you-- it'll remember so as you switch to that view, it'll use that undo manager.

It's the end to interface. When you undo you want to make sure that your user has some feedback when this happens. So don't undo offstage, you know. The user shakes the undo and nothing happens. So you want to, you scroll to make sure that's visible. You want to show a selection or something when that happens.

You know, you want to animate if possible, for example, if it's a graphic you moved around the screen, and you do an undo, you may want to show it either retracing its steps or just sliding back to the original location. You know, it's up to your application, but it's very nice to see, Oh that's what I did, and that's-- what's-- that's where it's back to its original place.

You want to make sure it doesn't undo somewhere else. As I said before, you want to sort of clear the undo state when it's logical to clear the undo state. You know maybe the user doesn't expect to be able to undo and they go back somewhere and suddenly they can't again. It's up to you, you know, to your application, but you want to do that.