Thursday, February 15, 2007

Using custom panels in Objective-C

Okay, so here was a problem that was presented to my by the Objective-C Language. Lets say in my .nib file, I have a custom panel to create a new person and add them to the database. In C#, I just call the name of the file to instantiate it. I tried to do the same in Objective-C. Well, there is a problem, a .nib file isn't really a class. It is a resource that contains GUI information. So, I had to sit down and figure out how to make my custom panel display on demand. After an hour, I figured out how.

To use a custom panel in Objective-C, you need to create it in your .nib file. After you do so, you need to create a handler for it and your main window. The easiest way to bypass this problem is to just create a reference (An Outlet with NSPanel or NSWindow for its type) to the window and the custom panel in the handlers. So then, if you want to make them appear all you have to do is call the method [panelName makeKeyAndOrderFront];

That's about it. A simple solution to the problem. Now I am sure that there is another way that is probably easier to work with, but this is a quick and pretty easy solution to my problem.

Hope this helps!

Tuesday, February 13, 2007

Classes in Objective-C

Something that confused me about Objective-C was how classes worked. I couldn't really find any good examples on it neither. So I sat down and played with it for a day and figured it out.

I will break down the structure for you and give you an example on how to create a working class in Objective-C.

  • In your XCode project, select File > New File
  • Scroll down to the Cocoa section and add a New Objective-C Class.
  • Change the name of the class to what ever you want, for this example, I will use ExampleClass.
  • Click create.
  • Now, in your project screen you will see two files called ExampleClass.h and ExampleClass.m
  • .h is your interface class. Providing the compiler with a brief overview of what your class has.
  • .m is your implementation class. This is where you will put your init and dealloc method as long as any methods you define in your interface class.
  • Within in your interface class (.h file) you will have the following:
@interface
{

}

@end

  • Anything placed inside the interface tag becomes attributes to the class. You can only put variables in here. Like an NSString, etc....
  • Methods are declared outside the interface brakets just right before the @end. These are just the method stubs without the { }'s

@interface
{
NSString *personName;
NSString *personAddress;
NSString *personPhone;
}
- (NSString *)PersonName;
- (NSString *)PersonAddress;
- (NSString *)PersonPhone;
- (void)setPersonName:(NSString *)value;
- (void)setPersonAddress:(NSString *)value;
- (void)setPersonPhone:(NSString *)value;

@end
  • Now to explain the code above: personName, personAddress, personPhone are only to the class. Nothing outside the class can access them. So to solve this problem and allow outside classes to communicate with these variables, you create Accessor methods. If you want a variable to be readable and writable from other classes, you need 2 methods, if you want a variable to be readable only, you need 1. Makes sense really.
  • As you can see, I didn't fill out the methods in this file. That is because it is a interface class and it can't do any work. It only tells the compiler what the class looks like.
  • To fill out the methods, you need to put them into the .m file and fill them out there.
  • In the .m file, add in the method stubs you created in the .h file.
  • After you do so, the .m file should look like this.
- (NSString *)PersonName
{
}
- (NSString *)PersonAddress
{
}
- (NSString *)PersonPhone
{
}
- (void)setPersonName:(NSString *)value
{
}
- (void)setPersonAddress:(NSString *)value
{
}
- (void)setPersonPhone:(NSString *)value
{
}
  • Now you need to fill them out. It should look like this when you are finished
- (NSString *)PersonName
{
return personName;
}
- (NSString *)PersonAddress
{
return personAddress;
}
- (NSString *)PersonPhone
{
return personPhone;
}
- (void)setPersonName:(NSString *)value
{
personName = value;
}
- (void)setPersonAddress:(NSString *)value
{
personAddress = value;
}
- (void)setPersonPhone:(NSString *)value
{
personPhone = value;
}
  • Now you need to add in your init and dealloc method. I can't remember what all is in these methods because XCode's CodeSense will plot it down for you if you type init and press return or dealloc and press return.
  • All you have to do is replace the <# Add Code Here #> tag here to have the compiler perform the the code on method when the class is loaded or thrown away.
That's it. All classes are made this way. It was confusing for me since C# classes just had a single file. Hope this helps!

Reading and Writing Data using Objective-C

Okay, Reading and writing data was something I made a big deal out of. Mainly because I started out with C# two years ago. In C#, you have to declare a StreamReader or a StreamWriter. After which, you then give it a location of a file and it returns a string based on the current line you are on. This made since once I figured it out. Then I got a mac a year back and wanted to figure out how to do the same in Objective-C. I tried the hardest method first thinking it was the easiest. You would think that you would do the same in Objective-C that you do in C#. Not so! All the work has been done for you!
Most objects which contain a NS at the beginning of the name have a method called writeToFile:(NSString *)aLocation.

And if they contain a writeToFile, they also contain a initWithContentsOfFile:(NSString *)aLocation.

So, with that said, this is how you will read in a file to a String.
NSString *filesContent = [[NSString alloc] initWithContentsOfFile:@"file.txt"];

To write a string to a file, well, That's easy too.

[yourDataString writeToFile:@"file.txt"];

Another thing I found to be the most useful in Objective-C is that when you write an array to a file, it converts it over to XML. If you have an array of objects, it will convert those objects to XML and put them in the list as well. This is great especially if you are creating a Mac and Windows app that you need to have working together. You can have your windows app conform to the mac's xml standard for writing arrays and you have two applications working on two systems that work off the same file format.

It is also wonderful because there is hardly any code needed to make it read and write data.

Apple has done it better again! :-)

NSTableView in Objective-C Explained

Okay,

Lets explain a bit on the NSTextView in Objective-C Example.

The Table view basically contains a dataSource method and a Delegate Method. It looks in the code you associate with it for that tableView Method and the numberOfRowsInTableView Method. It uses this to figure out what to display and how much to display. That's it. That's all it truly does. You need those to methods and provide the code to extract your information.

Unlike Objective-C, C# does all the work for you. All you have to do in C# is put the following to show a single item:
  • listView.Items.Add(aObject);
Basically, Microsoft did all the background work for you. You have to do the background work in Objective-C.

The benefits to the Objective-C form of doing a list or table are:
  • You get more control
  • Formatting is easier
  • Don't have to fool around with a dataset to display multiple items like in Objective-C
  • You can do some pretty neat things, like add images into the table with ease.
The Downside to this is:
  • It requires some setup and prior knowledge of Objective-C or a C Based computer language.
I hope that this has helped out you mac programmers out there who are interested in writing programs in Objective-C with tables!

Monday, February 12, 2007

NSTableView in Objective-C

Okay, lets go through a little example to help you understand just what a NSTableView is doing.

A NSTableView contains two layers, It contains a Data Layer and a Presentation Layer (The Actual TableView)

The datalayer is comprised of two methods that is in any of your classes. I will discuss this later in the example.

An example of this is:
Your monitor will visualize the data but it won't store it. The data is in the computer and is sent to your monitor for visualization.

Now to set this up, you need 3 steps. Step 1 is setting up you .nib file. Step 2 is setting up your data. Step 3 is creating your datasource for the table.

This is going to guide you through the steps to set up a new project that works with a NSTableView.

NOTE:You need to know how to work with XCode and Interface Builder to do this, if you don't know how, you might want to get a visual resouce to explain some of the stuff mentioned here.

Step 1.
  • Create a new NSDocument Based Cocoa App in XCode.
  • When your project is created, double-click the MyDocuments.nib file.
  • When Interface Builder loads you need to do the following:
  1. Remove the "This is a document" lable.
  2. Add a NSTableView Controller.
  3. Open the Inspector and set the columns field to be equal to 1
  4. Double-click in the table view to select the table view and not its NSScrollView Container
  5. Double-Click on the Column's Header to select the NSDataColumn
  6. In the inspector, change the Header to be "Text in Array" and set the identifier to be "text"
  7. Resize the column to look good.
  8. Add a Editable NSTextField (The Text Box)
  9. Add a NSButton and change its text to be something like "Add"
  10. Subclass the NSObject and create a new object called "TableViewController"
  11. Add 3 outlets named "tvTable" with type "NSTableView", "btnAdd" with type "NSButton", "txtTextToAdd" with type "NSTextField"; (You could keep the type as id, it really doesn't matter, this is just the way I like to do it)
  12. Add 1 action called "btnAdd_Click:"
  13. Instantiate and make your connections.After you have made all your connections, create your files.
  • Now you have created your .nib file and your class files. You now need to create your logic
  • Open up the newly created TableViewController.m file
  • You should see a method similar to this
- (IBAction)btnAdd_Click:(id)sender
{
}
  • If you don't see this, you will need to start over with the .nib creation.
  • Now, open up the TableViewController.h file
  • In the @interface section, you need to add the following.
NSMutableArray *arrayOfText;
  • Outside the @interface tags, add the following
- (int)numberOfRowsInTableView:(NSTableView *)tableView;
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex;
  • Build your project and close the .h file.
  • Now change your method stubs to look like this:
- (IBAction)btnAdd_Click:(id)sender
{
NSString *textToAdd = [txtTextToAdd stringValue];
[arrayOfText addObject:textToAdd];
[tvTable reloadData];
}

- (int)numberOfRowsInTableView:(NSTableView *)tableView
{
return [arrayOfText count];
}
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
{
NSString *valueToDisplay = [[arrayOfText objectAtIndex:rowIndex] string];
return valueToDisplay;
}
  • Now you need to add in 2 new methods (a init and a dealloc method)
  1. In the .m file, all you have to do is start typing init and wait for codesence to popup. It will too say init. Press enter and it will create your init method. The same will go with dealloc.
  2. Replace the <#Add Code Here#> text in the init method with this: arrayOfText = [[NSMutableArray alloc] init];
  3. Replace the <#Add Code Here#> text in the dealloc method with this: [arrayOfText release];
  • run the project and Wa La! You should now be able to type text into the text box, press the add button and everytime you do that, the text will then appear in the NSTextView.

Now you have finished the construction of the class, It seems like a lot of information but it is just hard to explain with short words. All table views work this way. If you wish, you can check the column's identifier and specifiy information to add based on the column, this makes your table more complex and in some way more beautiful because you can add in a image for a cell or even sliders, radio buttons, etc...

To keep this blog short, I will create another blog called NSTextView in Objective-C Explained.

Friday, February 9, 2007

NSSavePanel in Objective-C

Here is the code to use a basic NSOpenPanel.

1. NSSavePanel *save = [NSSavePanel savePanel];
2.
3. //Optional : Add Code here to change NSSavePanel basic configuration
4.
5. int result = [save runModal];
6.
7. if (result == NSOKButton){
8. NSString *selectedFile = [save filename];
9. //Add Additional code to handle the save;
10. }

Code Explained:
Line 1: When you declare a NSSavePanel, you need to make sure you put a *. Now to retrieve a basic save panel, you just need to use [NSSavePanel savePanel] and place it to the right of the =.

Line 3: You would replace this comment with additional configuration code. These are properties within the panel class like setRequiredFileType:NSString or setCanCreateDirectories:BOOL.

Line 5: When you are ready to present the panel, you will create a integer (int) called anything really, I am just using results since that is what it is storing. When the runModal method is called for the panel, it will return a int based on what the user did in the panel. Typically the user will press the OK Button or the Cancel Button.

Line 7: This is the check to see if the user clicked OK. NSOKButton is an enumeration (enum) that contains the int representation of a Dialog Result equal to OK. If the result is equal to this, then the user pressed okay and you can proceed with your code to handle the save. Now notice the = and the ==. They are used differently! The = means your are setting a value. The == means your are comparing values.

Thursday, February 8, 2007

NSOpenPanel in Objective-C

Here is the code to use a basic NSOpenPanel.

1. NSOpenPanel *open = [NSOpenPanel openPanel];
2.
3. //Optional : Add Code here to change NSOpenPanel basic configuration
4.
5. int result = [open runModal];
6.
7. if (result == NSOKButton){
8. NSString *selectedFile = [open filename];
9. //Add Additional code to handle the open;
10. }

Code Explained:
Line 1: When you declare a NSOpenPanel, you need to make sure you put a *. Now to retrieve a basic open panel, you just need to use [NSOpenPanel openPanel] and place it to the right of the =.

Line 3: You would replace this comment with additional configuration code. These are properties within the panel class like setCanChooseDirectories:BOOL or setAllowedFileTypes:NSArray

Line 5: When you are ready to present the panel, you will create a integer (int) called anything really, I am just using results since that is what it is storing. When the runModal method is called for the panel, it will return a int based on what the user did in the panel. Typically the user will press the OK Button or the Cancel Button.

Line 7: This is the check to see if the user clicked OK. NSOKButton is an enumeration (enum) that contains the int representation of a Dialog Result equal to OK. If the result is equal to this, then the user pressed okay and you can proceed with your code to handle the open. Now notice the = and the ==. They are used differently! The = means your are setting a value. The == means your are comparing values.

Using Objects in Objective-C

As you know, the mac programming language, Objective-C, handles things differently than C# for windows or Java. It took me a little time to understand how all this really works. Here is the syntax for using objects.

When you declare a object, you will need to use this following pattern:

NameOfObject *objectTitle;
- The * tells the compiler that you are creating a variable and not using one.

Accessing an object is differently than other languages too.
When you access an object, it is this following pattern:

[objectTitle methodName];
or
[objectTitle propertyName];

This essentially replaces the C# way of doing things, which is:
objectTitle.methodName();
or
objectTitle.propertyName;

Now when you declare something that is native to Objective-C's mother language C then you don't need the *.

For example:
double objectTitle;

Putting the * before the objectTitle will cause the compiler to freak out. It took me a couple of days to get this straight. Every time I used a double, I would do the * and wonder why it didn't work.

Hope this helps out the New-To-Mac Programmer's.

The Start

Hello,

I am here to introduce my first blog! I am creating this because I am a Mac Programmer, A new one at that, and I had problems trying to find out information about Cocoa, XCode, and Objective-C. Maybe someone trying to find answers might find them here on my blog.

I will make periodic posts with some information that I found out about Objective-C and Cocoa that I had a hard time understanding or even finding information on. I may even need some help with something. So, if you see this, check back soon and help me and others become a better Mac Programmer.