Posts Tagged ‘Xcode’
Undo and Redo
This topic is quite difficult to digest, but after few exercises you should be able to manage it.
When you work with an application, it usually happens that, after applying some edits to your data, you realize that the final result is not what you were expecting. So, you wish to go back and remove what you have done. I believe you are familiar with the Undo and Redo items in the Edit menu of your Mac. But, how can you implement these functionalities in your application?
Cocoa and Cocoa Touch use an intelligent approach to manage this. To understand it, I have to introduce the concept of NSInvocation. An NSInvocation is an object containing all the elements of an Objective-C message: a target, a selector, some arguments, and the return value. Each of these elements can be set directly and the return value is set automatically when the NSInvocation object is dispatched. NSInvocation works together with the NSUndoManager to make the Undo/Redo mechanism possible.
When you apply an edit onto your data and you want to be able to undo that edit, you need to prepare (coding) the inverse operation corresponding to that particular edit. Then, you pack this inverse message (including selector, arguments and return value) in an NSInvocation object and forward it to the NSUndoManager. This will store this object in a stack and wait for the user action.
Indeed, there are two stacks: the Undo Stack and the Redo Stack and their scope is very similar. The management of the two stacks is assigned to NSUndoManager and you don’t need to take care of it. When you undo some operation, NSUndoManager removes from the Undo Stack that operation and adds it to the Redo Stack. If you redo that operation, this time the NSUndoManager removes it from the Redo Stack and adds it to the Undo Stack.
Let’s look at the details and how to code it. Suppose you are writing a simple calculator with a plus and minus operations:
1 2 | - (NSNumber *)add:(NSNumber *)aNumber to:(NSNumber *)anotherNumber; - (NSNumber *)subtract:(NSNumber *)aNumber from:(NSNumber *)anotherNumber; |
You want to be able to undo and redo each of these operations after you apply them to your data. When you start your application, an NSUndoManager object is created for you with no costs. In reality, if you are writing a document-based application in Cocoa, an NSUndoManager is created automatically. In the other cases (iPhone included), you have to create it explicitly.
You usually need only an NSUndoManager for each application. To register the above two methods to the NSUndoManager mechanism, you do the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | - (NSNumber *)add:(NSNumber *)aNumber to:(NSNumber *)anotherNumber { NSUndoManager *undo = [self undoManager]; [[undo prepareWithInvocation] subtract:aNumber from:anothernumber]; float F1 = [aNumber floatValue]; float F2 = [anotherNumber floatValue]; F1 += F2; return [NSNumber numberWithFloat]; } - (NSNumber *)subtract:(NSNumber *)aNumber from:(NSNumber *)anotherNumber { NSUndoManager *undo = [self undoManager]; [[undo prepareWithInvocation] add:aNumber to:anotherNumber]; float F1 = [aNumber floatValue]; float F2 = [anotherNumber floatValue]; F1 -= F2; return [NSNumber numberWithFloat]; } |
The undoManger method (lines 3 and 4) is defined in the NSDocument, NSResponder, NSManagedObjectContext and WebView classes. Please, refer to the documentation of these classes for details.
The trick is at the lines 4 and 15. There, you register to the Undo/Redo mechanism the opposite operation to your current edit.
You can also change the label of the Undo and Redo operation using the
1 | - (void)setActionName:(NSString *)actionName; |
So, you can add this line after line 4:
1 | [undo setActionName:@"Add"]; |
Once implemented, you will have in the edit menu “Undo Add”.
WWDC 2009: The Keynote
I am not going to review all the new products announced during the keynote. I imagine you have already watched the keynote online and digested the fantastic news. Yes, you already know about the new MacBook Pro line, the Mac OS X Snow Leopard at 29$ and the new iPhone 3Gs, the Exchange integration, Grand Central Dispatch and OpenCL. And, if you don’t know what I am talking about, I suggest that you visit the Apple web site, immediately.
It has been a very long day today, but it was worth to come here. Things are becoming better and better for Mac and iPhone developers. The new versions of Xcode, Dashboard and Instruments (available in Snow Leopard) are amazing. I cannot disclose the details, because I am under NDA, but Xcode is in my opinion the best compiler on the market. Apple is reaching the top and nowadays, if you are a developer and you never tried to use the Apple developer Tools, I think you are at least 10 years behind.
When somebody tells me “Xcode is not so good as Visual Studio or Eclipse”, it means only one thing: she/he does not know how to use Xcode. I am going to install Snow Leopard on my main machine together with the new iPhone SDK. Because I want to take advantage of the new technologies as soon as possible. Now, it is also the right time to start again Mac development beside iPhone development.
iPhone User Alerts
In Cocoa, you can send alert to the user sending NSAlert class. In Cocoa Touch, you can achieve the same result using the UIAlertView and UIActionSheet classes.
UIAlertView
UIAlertView allows you to send messages to the user. To use an alert in your class, it should be conform to the UIAlertViewDelegate protocol. In this way, when one of the alert buttons is pressed, the following method can be called:
1 | - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex; |
To launch an alert is very simple. In your delegate class, you call the following method:
1 2 3 4 5 | - (id)initWithTitle:(NSString *)title message:(NSString *)message delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles,... |
As an example, you can call in your class the following lines of code:
1 2 3 4 5 6 7 8 | UIAlertView *alert; alert = [[UIAlertView alloc] initWithTitle:@”Alert” message:@”This is an alert” delegate:self cancelButtonTitle:@”Cancel” otherButtonTitles:nil]; [alert show]; [alert release]; |
Once the user has pressed the Cancel button, the previous delegate method -alertView:clickedButtonAtIndex is called and the user answer can be processed. The button index depends on the button position and the index starts with 0.
UIActionSheet
UIActionSheet works in the same way. In this case, the action sheet is needed to ask the user to take a decision. The action sheet can be visualized in a view, from a tab bar or from a navigation bar.
If your class uses an action sheet, it must be conform to the UIActionSheetDelegate protocol. In this way, it will be able to call
1 | -(void) actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex; |
and process the user action.
To launch an action is very simple too. In your delegate class, call the following method:
1 2 3 4 5 | -(id)initWithTitle:(NSString *)title delegate:(<UIActionSheetDelegate>)delegate cancelButtonTitle:(NSString *)cancelButtonTitle destructiveButtonTitle:(NSString *)destructiveButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... |
For example, you can do the following:
1 2 3 4 5 6 7 8 | UIActionSheet *action; action = [[UIActionSheet alloc] initWithTitle:@"Action" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"OK" otherButtonTitles:nil]; [action showInView:self]; [action release]; |
Example
Let’s make a small iPhone application using an alert view.
Open Xcode and create a Window-based application. Call it SimpleAlert. Open the SimpleAlertAppDelegate.h file and change it as it follows:
1 2 3 4 5 6 | #import @interface SimpleAlertAppDelegate : NSObject { UIWindow *window; } @property (nonatomic, retain) IBOutlet UIWindow *window; @end |
Notice the UIAlertViewDelegate protocol.
Now, change the SimpleAlertAppDelegate.m file. In the -applicationFinishLaunching method add the following:
1 2 3 4 5 6 7 8 | UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Alert" message:@"This is an alert" delegate:self cancelButtonTitle:nil otherButtonTitles:@"one",@"two",@"three",nil]; [alert show]; [alert release]; [window makeKeyAndVisible]; |
Then, implement the following method:
1 2 3 | - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { NSLog(@"You pressed button %d",buttonIndex+1); } |
Build and Run. The following alert will appear on your iPhone.

When you press one of the button a message should appear in the console.
Have a nice coding.
++Geppy



