Thursday, May 6, 2010

Creating a Navigation-Based Application

In this example I'll show you how to create an application that will:
  • Use a Navigation-Based interface.
  • Switch between different views using the Navigation-Based application.
  • Send messages to different parts of the application using the Notification Center.

Creating a new Navigation-Based Application.

Open Xcode and select New Project from the File menu.
Select Navigation-Based Application and click on Choose.

Name it NavigationApplication.


Creating the necessary files.

On the Xcode window Ctrl + Click the Classes folder and select Add > New File.
Choose UIViewController subclass from the Cocoa Touch Classes. Make sure the "With XIB for user interface" is checked. 

Name this class ViewOneController.


Create another class and name it ViewTwoController.

Move the .xib files to the Resources folder.


With this we have created two views and their respective controller classes.

Editing the different views from the Interface Builder.

Double click on ViewOneController.xib to open up the Interface Builder. Press Command + Shift + L to bring up the Library window. Add a Button and a Text View to the view. Edit your view to make it look like this (you can press Command + 1 to bring up the Attributes Inspector if needed):

Click on File's Owner and press Command + 4 to show the Identity Inspector window. 
On the Class Identity section of the window change the Class to ViewOneController.



Click on the arrow next to ViewOneController. Add an action named sendMessage: and an outlet named textView of type UITextView.





Press Command + S to save and then select Write Class Files from the File menu.
Click on Save then Merge. Choose left for both differences on ViewOneController.h and left for the first difference on ViewOneController.m

Click on File's Owner again and press Command + 2 to show the Connection Inspector.
Link textView with the Text View from the view.


Now link sendMessage: with the button from the view.


On the actions from the button select Touch Up Inside.

Link view with... the View...



Press Command + S to save and go back to Xcode.

Now open the ViewTwoController.xib. Add a Label to the view and write "Received Message:" in it. Add a Text View to the view and remove the text in it. Your view should look like this:


Click on File's Owner and press Command + 4 to bring up the Identity Inspector. Select ViewTwoController in Class.

Create a new outlet and name it receivedText and make it of type UITextView.



Press Command + S to save and click on File > Write Class Files. Click on save and then merge. Select the left file for ViewTwoController.h.

Click on File's Owner and press Command + 2.
Link receivedText with the Text View...



and also link view with the View.

Save again and exit the interface builder.


Adding the navigation code to the application.

Go back to Xcode and open RootViewController.h.
Make your code look like this:

#import <UIKit/UIKit.h>

@interface RootViewController : UITableViewController {
NSMutableArray *navigationArray;
}

@property (nonatomic,retain) NSMutableArray *navigationArray;

@end
We created an array called navigationArray. This array will be used to store information that is needed to navigate trough the views in our app.

Now open RootViewController.m and add the following imports:

#import "ViewOneController.h";
#import "ViewTwoController.h";

And add the following method:

//awakeFromNib is called when a xib or nib file is initialized
-(void) awakeFromNib{
//this array is used to add the views that we want to access from
//the navigation controller
//even though the menu items aren't added directly to the table in this method
navigationArray = [[NSMutableArray alloc] init];
//initialize the first view and add it to the table array
ViewOneController *viewOneController = [[ViewOneController alloc] init];
viewOneController.title = @"View One";
[navigationArray addObject:
[NSDictionary dictionaryWithObjectsAndKeys:
@"View One", @"title", viewOneController, @"controller", nil]];
[viewOneController release];
//initialize the second view and add it to the table array
ViewTwoController *viewTwoController = [[ViewTwoController alloc] init];
viewTwoController.title = @"View Two";
[navigationArray addObject:
[NSDictionary dictionaryWithObjectsAndKeys:
  @"View Two", @"title", viewTwoController, @"controller", nil]];
[viewTwoController release];
//The title is needed if we want to be able to navigate back
//to the menu from the other views
self.title = @"Main";
}

Search for the method tableView:numberOfRowsInSection: and change the code to this:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [navigationArray count];
}
With this method we change the number of rows in the navigation menu. Since the navigationArray only has 2 entries we could change this to " return 2; " but is better to leave it this way in case we want to add more views in the future.

On the method next to this one, tableView:cellForRowAtIndexPath: , add the following code below the //Set up the cell... comment.

    // Set up the cell...
[cell.textLabel setText:[[navigationArray objectAtIndex:indexPath.row] objectForKey:@"title"]];


This adds the title for each view to the navigation menu.

Now go to the method tableView:didSelectRowAtIndexPath: and edit the code to make it look like this:






- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
UIViewController *anotherViewController = [[navigationArray objectAtIndex:indexPath.rowobjectForKey:@"controller"];
[self.navigationController pushViewController:anotherViewController animated:YES];
}

This adds the logic for the navigation.

Finally, go to the dealloc method and add this line:
[navigationArray release]; 
*It is necessary to add a "release" for the variables where we use "retain".

Testing the navigation interface.

You can try running the application now. Press Command + R to run the simulator.


Try navigating trough the app. The logic for sending a message isn't there yet, but you can try using the elements in the view anyway.




*If the keyboard blocks the button when you click/touch a Text View you can try setting your button a little higher. I'll explain a method to hide the keyboard at the end of this tutorial.

Adding the code to enable message sending.

Let's add the code for sending messages between the views.
Open NavigationApplicationAppDelegate.h and make your code look like the following.

#import <UIKit/UIKit.h>

@interface NavigationApplicationAppDelegate : NSObject {
    NSString *message;
    UIWindow *window;
    UINavigationController *navigationController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UINavigationController *navigationController;

-(void)setMessage: (NSString *)newMessage;
-(NSString *)getMessage;

@end

The message variable is used to save the information that we want to share between the two views.

Add the following methods to NavigationApplicationAppDelegate.m.

- (void)setMessage:(NSString *)newMessage{
message = newMessage;
[[NSNotificationCenter defaultCenter] postNotificationName:@"messageSentEvent" object:self]; 
}


- (NSString *)getMessage{
[message retain];
return message;
}

 As you can see the setMessage: method sends a notification to the notification center. We will add an observer to View Two so it is notified when a different message is set.  The retain method is called by getMessage to help the object "retain" the value of this variable. If this method is not called the application may not function correctly. (You can try removing this line of code and sending a lot of messages when the application is finished if you want to see the effects of this.)

We need to make one last change to this file. Add the bold line to the dealloc method.

- (void)dealloc {
[message release];
[navigationController release];
[window release];
[super dealloc];
}
A release needs to be added because we used the retain method on message.

Now open ViewOneController.m. Add the following import:

#import "NavigationApplicationAppDelegate.h";

Add the following code to the sendMessage: method:

- (IBAction)sendMessage:(id)sender {
//gets the delegate for this application
    NavigationApplicationAppDelegate *navigationAppDelegate = 
(NavigationApplicationAppDelegate *)[[UIApplication sharedApplication] delegate];
//set the string value
NSString *newMessage = textView.text;
//call the setMEssage method from the delegate
[navigationAppDelegate setMessage:newMessage];
//hide the keyboard
[textView resignFirstResponder];
}
Open ViewTwoController.m and add the following import:

#import "NavigationApplicationAppDelegate.h"
Now add the following methods:

//this method is used so an observer can be added as soon as this object is initialized
-(id) init { 
self = [super init]; 
if (self != nil) { 
// add an observer
[[NSNotificationCenter defaultCenter] addObserver:self 
selector:@selector(messageReceived) name:@"messageSentEvent" object:nil]; 
}
return self;
}

//gets the message everytime a message is received
-(void) messageReceived{
NavigationApplicationAppDelegate *NavigationAppDelegate =
[[UIApplication sharedApplication] delegate];
[receivedText setText: [NavigationAppDelegate getMessage]];
}

//gets the message when the view is first loaded
-(void)viewWillAppear:(BOOL)animated { 
NavigationApplicationAppDelegate *NavigationAppDelegate =
[[UIApplication sharedApplication] delegate];
[receivedText setText: [NavigationAppDelegate getMessage]];

}

When the object is initialized an observer will be added. This observer specifies that every time a notification with the name messageSentEvent is received the method messageReceived will be executed.

Observers are used to subscribe to the notification center. After subscribing, the notification center will be in charge of notifying the subscribing objects when a new event occurs.

Testing the complete application.

Let's run the application. Press Command + R to compile and start the simulator.

Go to the first view and write a message. After writing the message click / touch on "Send Message".


Go back to the main menu and then to the second view.

As you can see the message was succesfully received by the second view.

Extra: Adding a hidden button to hide the keyboard.

The keyboard that appears when you click / touch over Text Views and Text Fields can be really annoying sometimes. I'll show you a method to hide the keyboard... even though...  It might not be a very orthodox way to do this.

Double click on ViewOneController.xib to open the Interface Builder.

Select all the elements from the view and press Command + x to cut them. Press Command + Shift + L to bring up the Library window. Now add a button to the view and expand it to fill the whole view.

Press Command + 1 to show the Attributes Inspector. Select Custom in Button Type.


By using Custom as a Type for the Button the button becomes invisible. Now click on the view title bar and press Ctrl + V to paste the elements you cutted before. Be sure to place them correctly over the view since they might appear a little messed up at first.

Now click on File's Owner and press Command + 2 to show the Connection Inspector.
Re-link the outlets to the elements on the view.

textView -> Text View on the View

sendMessage: -> Button and then Touch Up Inside


Press Command + 4 to go to the Identity Inspector window. Click on the arrow next to the Class name and create a new action named clickOnBackgroundButton: . Save and Write Class Files.

Select left for the only difference in ViewOneController.h, click on the red button and save the file.
Select left too for ViewOneController.m and save the file.

Select the Connection Inspector again and link the clickOnBackgroundButton: action to the background of the view (which this time isn't the view itself, but a hidden button).



When prompted select Touch Up Inside.


Save and exit Interface Builder.

Go back to Xcode and to ViewOneController.m.

Add the following code to the clickOnBackgroundButton: method:

- (IBAction)clickOnBackgroundButton:(id)sender {
    [textView resignFirstResponder];
}
Yup, that's the only line of code you need to add. If you were to have various Text Views or Text Fields in your view, you would need to call the resignFirstResponder method for each of those elements.

Run your application and click on the Text View from the first view. Now click the background. This time the keyboard should hide automatically without the need of pushing the Send Message button.

*Note: Remember the Text View is on top of the hidden button, if you press over it the keyboard won't hide.

And well... that's it for this example. I hope you learned how to create a Navigation-Based application and how to use the Notification Center.

Wednesday, May 5, 2010

Creating a "view based" iPhone application

In this example you'll learn how to:
  • Create an iPhone application.
  • Design a simple cocoa touch interface.
  • Link objects in the interface to objects and methods in the code.


Open Xcode and select new project from the file menu.
Select iPhone OS from the menu and then View-Based Application. Check that "iPhone" is selected on Product.



Save as: ViewBasedApplication.
Click on Save.


From the window that appears, double-click on Resources.
There are 2 .xib files. Xib files are xml files that are used to define user interfaces for iphone and os x applications.



As you can see there is one file called MainWindow and one called ViewBasedApplicationViewController. The MainWindow file contains the "Main Window" for your iPhone application. This window will be called when your application starts running. A window is used to load views on an iPhone application. Most applications will use one main window and use different views to display all the screens your application might have.

Now, double-click on the ViewBasedApplicationViewController.xib file to open up the interface builder. Interface Builder is the application you will be using to build the interfaces for your iPhone apps.

Click on the view window.

Press Command + Shift + L to bring up the library window. Write label on the filter field. A label will appear on the window. Drag and drop it to the view window.

Go back to the library window and write button. Take the button and drop it on the view window too.
Click on an element from your view and press Command + 1 to bring up the attributes from the element. From here you can edit how elements from the view look. Try to make your view look like this.



Now, let me explain something here. Everything you do in the interface builder is completely separated from your Objective-C code. So, the elements you add to your view or window won't be accessible from the code at first. For them to be accessible from the code you have to declare an equivalent object in your code and then "link it" to the interface using the interface builder. We will see how to do this next.

Go to the window that says ViewBasedApplicationViewController.xib and click on File's owner.
Press command + 4 to bring up the identity inspector.

Click on the arrow on the Class section of the window.



The class ViewBasedApplicationViewController will open on the Library Window.




Click on Outlets and then on the + . Write label for the outlet and UILabel instead of id for the type. Add another entry for button.



Now go to Actions and click on the +. Add a new action named doSomething: .



Press Command + S to save. Now click on File's Owner again, go to the file menu and click on write class files. Click on save and then select merge.

For ViewBasedApplicationViewController.h select left for both differences from the Actions menu (bottom right corner of the window) .

For ViewBasedApplicationViewController.m select left for the first difference and anything for the second (since it's the same code, the only difference are the blank lines).

Click on the red button and then save each file.

Now we have to link the elements from our view to the ones we just created in our class files.
Go to the view and ctrl + click on the label. Click on the circle from new referencing outlet.



Drag the mouse to File's Owner.


Click on label.

Now the label from the view is correctly linked to the label in the code. So, when you update  the label in the code the label from the view will be updated too.

Let's link the button and the action from the button too. This time I'll show you a different way to do it, but you can do it however you like.

Click on File's Owner and press Command + 2. This will bring up the Connections Inspector window. Click on the circle next to button and drag it to the button on the view.



If you did it correctly it will look like this.


Now click on the circle next to doSomething: and drag it to the button too. Select Touch Up Inside.




Now the actions and elements from our view are correctly linked to the ones on our code. Press Command + S to save.

Go to Xcode and open ViewBasedApplicationViewController.h. And change your code so it looks like this:

#import <UIKit/UIKit.h>

@interface ViewBasedApplicationViewController : UIViewController {
    IBOutlet UIButton *button;
    IBOutlet UILabel *label;
    int counter;
}
- (IBAction)doSomething:(id)sender;


@end

The only line you need to add is int counter; .

Open ViewBasedApplicationController.m and add the following code to the doSomething: method.

- (IBAction)doSomething:(id)sender {
    counter++;
    label.text = [[NSNumber numberWithInt:counter] stringValue];
    [button setTitle:@"Pressed" forState:0];
    [button setTitle:@"Pressed" forState:1];
}


The doSomething: method will be executed every time the button is pressed. The counter++; is used to add 1 to the counter variable. After that, the value of counter is added to the label. 

The lines:
[button setTitle:@"Pressed" forState:0];
[button setTitle:@"Pressed" forState:1];
are used to change the text (Title) from the button. As you can see the text needs to be changed 2 times, one for each state of the button. State: 0 is used when the button is unpressed, and State: 1 is used when the button is pressed. You can add different text to the button for each state.

Press Command + R to compile your code and launch the iPhone simulator.

Your application will look like these at first:

But after clicking the button the text from the button will change and the label will change too.


And that's all you need to do to create a simple view based application.

Wednesday, April 28, 2010

Quick Objective-C example

In this example we will see how to send multiple parameters to a method and how to create a conscructor method inside a class.


Create a new project in xcode and name it example2.
Now, create a new class named TestClass.

TestClass.h


Two variables are created: testVariable1 and testVariable2.

Two methods are also created.


TestClass.m


main.m


The first method is used to receive two parameters and assign them to their respective variables. The name of the method is extended for it to accommodate various parameters. The full name of this method is " setTestVariable1:and2: ". The part of the name after the first colon could be omitted in the declaration but it is recommended to use it since it gives us some insight about what kind of parameter the function needs to receive.

The " print " method is used to print the results of the two variables.

Let's test this to check that we did everything right.
Compile your project by pressing "Command + B" on Xcode. Press "Command + Shift + R" to open the Console and "Command + R" to Run your application.


Now we'll add a constructor method to this example. 
A constructor method lets you initialize the variables in an object at the same time that the object is initialized.


TestClass.h




TestClass.m



The name of the constructor method is initWithTestVariable1:and2:.This method initializes itselft, and then assigns values to its variables using the same method we used at the beginning of this example(setTestVariable1:and2:).


main.m


Run this again to test it.




The constructor method is used instead of init on the first statement. By doing this, the values of testVariable1 and testVariable2 are assigned in the same statement where the object is initialized.