Pages

Friday, July 30, 2010

Create UIView, UILabel and UIButton programmatically.

A year ago, when I needed to make my first iPhone application, Interface Builder looked for me very complicated. I came from the Microsoft Windows Mobile world where we created all GUI created programmatically (the standard controls are very poor, MFC worked too slow, etc.). My task has nothing to do with the GUI. I needed a test-wrapper for my code that was supposed to test a 3rd party framework. Google helped me to find few tutorials but all of then proposed to work with Interface Builder. It is really nice and simple to choose a control and put it on my application window in the Interface Builder. But how to connect this control with my code? In the beginning it was a kind of a rebus I had to solve. It was not very obvious for me to catch all these outlets, actions and bindings, Command- and Control-clicks, six different Inspectors,...
I hope this article will help to someone who likes me a year ago is looking for a way to make his first program without Interface Builder.

Let's assume that we know nothing about Xcode, Objective-C and Cocoa Touch - these are my conditions a year ago.

The following steps will help us to create a simple iPhone program with a label and a button:

1. Create Window-based iPhone application.
Launch Xcode. In menu File choose item "New Project...". In the project wizard that comes next, in the left panel select "Application" item in the "iPhone OS" section. Select "Windows-based Application" icon on the right and press "Choose". Next window asks to set a name for the project. Type "First" and press "Save" button. By default the project is created in the "My Document" folder.

2. Add UIView programatically.
In the project window, on the left panel that looks like the Finder sidebar you need to find FirstAppDelegate.m file. This sidebar represents as a tree all files of the project. The root of that tree is the project file. You can open this item and all others in the same was as you do with the folder in the Finder. If you have found the FirstAppDelegate.m file, click on it and its content will be shown in the right panel. There is a lot of code but we need only one function application didFinishLaunchingWithOptions. Make it look so:
/- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:
(NSDictionary *)launchOptions
{
// Window bounds.
CGRect bounds = window.bounds;

// Create a view and add it to the window.
UIView* view = [[UIView alloc] initWithFrame: bounds];
[view setBackgroundColor: [UIColor yellowColor]];
[window addSubview: view];

[window makeKeyAndVisible];

[view release];

return YES;
}

3. Build and Run.
In Xcode, in project window, in the left-up corner you can a toolbar that now, probably, contains text "Device - 4.0. Debug. First". It is a popup button. You need to click on it and select Simulator. Now press on "Build and Run" button. If you see the simulator ruuning, if you see an ugly yellow window on it - you did everything perfect. Close the simulator and go back to Xcode - there is "Quit" item in the main menu of the simulator.

4. Add a label.
Here is the code that should be added to the same function after the line [window addSubview: view};:
CGRect labelFrame = CGRectMake( 10, 40, 100, 30 );
UILabel* label = [[UILabel alloc] initWithFrame: labelFrame];
[label setText: @"My Label"];
[label setTextColor: [UIColor orangeColor]];
[view addSubview: label];
You can Build and Run the program again to check the label is on the application screen.

5. Create new button.
After the label code in the same function we need to add the following:
CGRect buttonFrame = CGRectMake( 10, 80, 100, 30 );
UIButton *button = [[UIButton alloc] initWithFrame: buttonFrame];
[button setTitle: @"My Button" forState: UIControlStateNormal];
[button setTitleColor: [UIColor redColor] forState: UIControlStateNormal];
[view addSubview: button];
Build and Run the program. These orange label and white button with the red title is our art.
If something goes wrong, verify our working function:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:
(NSDictionary *)launchOptions
{

// Override point for customization after application launch.


// Window bounds.
CGRect bounds = window.bounds;

// Create a view and add it to the window.
UIView* view = [[UIView alloc] initWithFrame: bounds];
[view setBackgroundColor: [UIColor yellowColor]];
[window addSubview: view];

// Create a label and add it to the view.
CGRect labelFrame = CGRectMake( 10, 40, 100, 30 );
UILabel* label = [[UILabel alloc] initWithFrame: labelFrame];
[label setText: @"My Label"];
[label setTextColor: [UIColor orangeColor]];
[view addSubview: label];

// Create a button and add it to the window.
CGRect buttonFrame = CGRectMake( 10, 80, 100, 30 );
UIButton *button = [[UIButton alloc] initWithFrame: buttonFrame];
[button setTitle: @"My Button" forState: UIControlStateNormal];
[button setTitleColor: [UIColor redColor] forState: UIControlStateNormal];
[view addSubview: button];

[window makeKeyAndVisible];

[button release];
[label release];
[view release];

return YES;
}

6. Add an action.
In our program the button does nothing. Let's add an action. In the header file (FirstAppDelegate.h) you need to add one line:
- (void)buttonClicked: (id)sender;
In Xcode, Alt-Cmd-Up keyboard shortcut will switch the editor to the header file from the m-file. If you have added this line, use the same keyboard shortcut to switch back to the m-file. and implement the action:
- (void) buttonClicked: (id)sender
{
NSLog( @"Button clicked." );
}
Now modify the code creating the button and add the following code:
[button addTarget: self 
action: @selector(buttonClicked:)
forControlEvents: UIControlEventTouchDown];
Now if you will build and run the program in the Xcode console you will see "Button clicked" text.

Tuesday, July 27, 2010

Cocoa: Implicit Animation

This program will help to begin with the Core Animation.
1. In Xcode create Max OS X Cocoa Application.
2. In the Application delegate implementation file (automatically created by Xcode on Snow Leopard) add a button to the content view:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{
// Create new button in content view.
NSRect frame = NSMakeRect(10, 40, 90, 40);
NSButton* pushButton = [[NSButton alloc] initWithFrame: frame];
pushButton.bezelStyle = NSRoundedBezelStyle;
[pushButton setTitle: @"Move"];
[self.window.contentView addSubview: pushButton];

// Set the button target and action.
pushButton.target = self;
pushButton.action = @selector(move:);

[pushButton release];
}
3. As you see from the code above the method -move is set as the button action. So add IBAction move to the application delegate class. The interface should look so:
#import <Cocoa/Cocoa.h>

@interface ImplicitAnimationAppDelegate : NSObject <NSApplicationDelegate>

@property (assign) IBOutlet NSWindow *window;

- (IBAction)move: (id)sender;

@end
And -move method in the implementation file:
- (IBAction)move: (id) sender
{
// The button frame.
NSRect senderFrame = [sender frame];

//The view bounds. The button belongs to this view.
NSRect superBounds = [[sender superview] bounds];

// Calculate new position within the view bounds.
senderFrame.origin.x = (superBounds.size.width -
senderFrame.size.width) * drand48();
senderFrame.origin.y = (superBounds.size.height -
senderFrame.size.height) * drand48();

// Move the button.
//[sender setFrame: senderFrame];
[[sender animator] setFrame: senderFrame];
}
The commented line [sender setFrame: senderFrame]; simply moves the button. Line [[sender animator] setFrame: senderFrame]; creates an animation effect - the location change was split up for few movements.
Short description: each thread maintain an automation context. It is possible to object the object of this context as:
[NSAnimationContext currentContext];
Then, it is possible to customize this object, for example, change the duration:
[[NSAnimation currentContext] setDuration: 1.0];

Monday, July 26, 2010

Jailbreak is legal?

I even don't know if it is true:
Now Legal to Jailbreak Your iPhone in the U.S.

"Not only is jailbreaking now okay, but ripping DVDs and cracking video game or software encryption is too, in certain special circumstances. It doesn’t exactly mean it’s open season for any and all piracy, but it does relax things quite a bit, and will probably make it much harder to prosecute those kinds of violations."

More about it:
Money. CNN. Jailbreaking iPhone apps is now legal
EFF Wins New Legal Protections for Video Artists, Cell Phone Jailbreakers, and Unlockers

Sunday, July 25, 2010

Template program to learn Cocoa graphics

The standard way to learn new programming language is very boring for me - endless reading of heavy books, typing useless programs that calculates factorials,... Since my student times, since GWBASIC I begin from the graphics, simple graphic, rectangles, circles - the graphic primitives. When I know how to draw them, I can go on and learn the basic language constructions, language semantic and programming techniques.
This way works for me in my Cocoa period. In this post I'd like to show two methods to create a template project that allows to learn the Cocoa graphical primitives. This template can grow in your hands and become a real Cocoa (or Coco Touch) application.
As any Cocoa application this application has a main window and a view inside. That's all. First method uses Interface Builder. The second one fully ignores the Interface Builder.

Method with Interface Builder.
1. Create new project in Xcode from the Cocoa Application template, add new Objective-C class derived from NSView, give a name to this class, for example DrawingView.
2. Launch Interface Builder by pressing on the MainMenu.xib file. Find "custom view" in the Library, drag and drop it onto the application window. Set the view size - almost the whole window. In the Size Inspector (Cmd+3) set the autosizing options. In the Identity Inspector (Cmd+6) set the view name as DrawingView. Save the project (Ctrl+S).
3. Switch to Xcode and open DrawingView.m file. In -drawRect method we can add our drawing code. For example:
- (void)drawRect:(NSRect)dirtyRect {
// Drawing code here.

[NSGraphicsContext saveGraphicsState];

NSRect rect = NSInsetRect([self bounds], 5, 5 );
[[NSColor blueColor] set];
NSRectFill( rect );

[NSGraphicsContext restoreGraphicsState];
}
The application runs:

Method without Interface Builder.
1. The first item is the same - create new project in Xcode from the Cocoa Application template, add new Objective-C class derived from NSView.
2. Open the implementation of the application delegate class (AppDelegate.m) and create the view object in the launching method:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
DrawingView *view = [[DrawingView alloc]
initWithFrame: [self.window.contentView bounds]];
[self.window.contentView addSubview: view];
[view setAutoresizingMask: ( NSViewWidthSizable | NSViewHeightSizable) ];
[view release];
}
3. Open the view implementation class and add the drawing code.
Launch the application.

Sunday, July 18, 2010

Cocoa: NSScanner

sscanf is a standard C function. We use it so rarely, but it exists and can be the fastest method to parse a string. In order to remind I post this short program that uses sscanf to retrieve two float number from a string:
#include <stdio.h>

int main (int argc, const char * argv[])
{
float x, y;
const char* string = "3.1415 6.28";
sscanf(string, "%f %f", &x, &y);
printf("x = %.4f, y = %.2f\n", x, y);
return 0;
}

How to parse this string in Objective-C?
Firstly, this is Objective-C, so the same code will work:
#import <Foundation/Foundation.h>
#include <stdio.h>

int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

float x,y;
const char* string = "3.1415 6.28";
sscanf(string, "%f %f", &x, &y);

NSLog(@"x = %.4f, y = %.2f", x, y);

[pool drain];
return 0;
}
Secondly, this is Objectve-C. So there is a special class NSScanner that does the work.
#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

float x, y;
NSString* str = @"3.1415 6.28";
NSScanner* scanner = [NSScanner scannerWithString:str];
[scanner scanFloat: &x];
[scanner scanFloat: &y];
NSLog(@"x = %.4f, y = %.2f", x, y);
[pool drain];
return 0;
}

Let's make a more complicated example. The following program will create a text file and save three basic graphical structures in it: NSPoint, NSSize and NSRect.
#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

NSString* fileName = @"coordinate.txt";
NSError* err = nil;

NSPoint point;
NSSize size;
NSRect rect;

// Write NSPoint, NSSize and NSRect to file.
point = NSMakePoint( 3.1415, 6.28 );
size = NSMakeSize( 2, 14 );
rect = NSMakeRect( 10, 10, 200, 100 );

NSString* outString = [NSString 
stringWithFormat:@"point = %@; size = %@; rectangle = %@;",
      NSStringFromPoint(point), NSStringFromSize(size), NSStringFromRect(rect) ];

[outString writeToFile: fileName 
atomically: NO encoding: NSASCIIStringEncoding error: &err];

[pool drain];
return 0;
}
The output file looks so:

Of course, this file can be parsed in the standard C with the simple sscanf. The next Objective-C program does it with NSScanner class:

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

NSString* fileName = @"coordinate.txt";
NSError* err = nil;

NSPoint point;
NSSize size;
NSRect rect;

NSStringEncoding encoding;
NSString* inString = [NSString stringWithContentsOfFile: fileName
usedEncoding: &encoding
error: &err];

NSString* strPoint = @"point =";
NSString* strSize = @"size =";
NSString* strRect = @"rectangle =";

if ( err == nil )
{
if ([inString length] > 0)
{
NSString* str;
NSScanner* scanner = [NSScanner scannerWithString: inString];

if ( [ scanner scanString: strPoint intoString: NULL] )
{
[scanner scanUpToString: @";" intoString: &str];
[scanner scanString: @";" intoString: NULL];
point = NSPointFromString( str );
}

if ( [scanner scanString: strSize intoString: NULL])
{
[scanner scanUpToString: @";" intoString: &str];
[scanner scanString: @";" intoString: NULL];
size = NSSizeFromString( str );
}

if ( [scanner scanString:strRect intoString: NULL] )
{
str = [inString substringFromIndex: [scanner scanLocation]];
rect = NSRectFromString( str );
}

NSLog( @"point = %@", NSStringFromPoint( point ) );
NSLog( @"size = %@", NSStringFromSize( size ) );
NSLog( @"rectangle = %@", NSStringFromRect( rect ) );
}
}

[pool drain];
return 0;
}
Program output:

Program loaded.
run
[Switching to process 57339]
Running…
2010-07-18 21:48:24.201 CoordinatesToString[57339:a0f] point = {3.1415, 6.28}
2010-07-18 21:48:24.205 CoordinatesToString[57339:a0f] size = {2, 14}
2010-07-18 21:48:24.206 CoordinatesToString[57339:a0f] rectangle = {{10, 10}, {200, 100}}

Mac OS X Reference Library:
NSScanner Class Reference
String Programming Guide. Scanners.

Friday, July 9, 2010

Cocoa: show Alert

Show Alert in a Cocoa application:


- (IBAction)showAlert:(id)sender
{
NSString *question = NSLocalizedString(@"Do you see this alert?"
  @"Let's verify that I see this question");
NSString *info = NSLocalizedString(@"I hope, I see this alert"
           @"Here is an info");
NSString *cancelButton = NSLocalizedString(@"Cancel"
  @"Cancel button title");
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:question];
[alert setInformativeText:info];
[alert addButtonWithTitle:cancelButton];
NSInteger answer = [alert runModal];
[alert release];
alert = nil;
}

Cocoa: Binding. GUI application without outlets

Outlet in Cocoa is a persistent reference to a GUI control. For example, it is a common way to create the outlet to the text field and change the text in this field via the outlet. Now, in 64-bit Xcode, you add a property with IBOutlet keyword, synthesize it, and set new text via that property:

1. In interface declaration:
@property (retain) IBOutlet NSTextField * text;

2. In the implementation section:
@synthesize text;

3. Set new text to the text field:



text.stringValue = @"Hello, World!";


4. Get value:

NSString* str = text.stringValue;

Same can be done without coding. Sometimes it can be needed and interesting. Let's make a test application:
1. Create new Mac OS X Cocoa project in Xcode:

I named the project as TextNoOutlet (shown on the screenshots above).
2. In the application delegate class we add a string property:

Initially the application delegate class declared as (TextNoOutletAppDelegate.h file):

#import

@interface TextNoOutletAppDelegate : NSObject {
    NSWindow *window;
}

@property (assign) IBOutlet NSWindow *window;

@end

Replace it for:

#import

@interface TextNoOutletAppDelegate : NSObject

@property (assign) IBOutlet NSWindow *window;
@property (copy) NSString *text;

@end


Control-S to save the header file. Alt-Cmd-Up to switch to the m-file. Synthesize the property:

#import "TextNoOutletAppDelegate.h"

@implementation TextNoOutletAppDelegate

@synthesize window;
@synthesize text;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application 
}

@end


Here I added only one line shown in bold. Control-S here to save the modification.

3. In Interface Builder (click twice on the MainWindow.xib file) will put the Text Field on the prototype window. Let's put Label under this text field. Arrange the size and locations.

4. The binding.
Select the text field in the prototype window. Press Command-4 to open the Binding Inspector:

In the Value section check the checkbox and set "text" in the Modal Key Path field.


Select the label. Command-3 to switch to the Binding Inspector. Check the checkbox and modify the Modal Key Path in the same way as for the text field.

Save your work in Interface Builder (Control-S or Save in the File menu).
5. Build and Run the program:


Try to type a text in the text field, press Enter and see the text reflected in the label.

Let's apply the binding to the application window:
In Interface Builder select the window object in the Document window, press Command-4 to switch to the Binding Inspector and bind the title of the window in the same way as the label before:


Save the change and Build and Run the program:


I'd Like to show one more interesting example with the slider control.  Same principle: we will add a property, for example value to the application delegate class and bind it with the slider control and a label. The property added to the application delegate is shown in the bold:

#import

@interface TextNoOutletAppDelegate : NSObject

@property (assign) IBOutlet NSWindow *window;
@property (copy) NSString *text;
@property (assign) NSInteger value;

@end


It should be synthesized in the implementation file:

#import "TextNoOutletAppDelegate.h"

@implementation TextNoOutletAppDelegate

@synthesize window;
@synthesize text;
@synthesize value;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application 
}

@end


In Interface Builder I add the slider and one more Label. Then I bind the slider with "value" property:


I'd like to set "Continuos" for the slider control:


Now I select the new label in the prototype window and will bind it in a bit different way - via "Value with Pattern"


Save my work, switch to Xcode and Build and Run my program:

This "simplification" of the GUI work (do not use outlet for a GUI control and connect many GUI controls to the same property) is possible because of the nice feature of Objective-C 2.0 - Key-Value Coding or KVC. It is a simple protocol allowing to set and get values indirectly. In the first example, that uses the outlet, I call a method to get or to set values to/from the text field. Then, in the next examples, I specify a key that is used by Cocoa to figure out which property match up to this key: the property text was added to the application delegate class and key 'text' was used to bind the GUI controls with the application delegate.

Sunday, July 4, 2010

Simple program to test NSDateFormatter

It is a pleasure to make short and simple program in Xcode 3.2.2. You choose Cocoa Application template and it already contains the application delegate class. The class is very simple, but now I don't need to remember this strange procedure of adding the application controller class and connecting it with the Interface Builder - a year ago I spent hours to get and remember this boring sequence.
One more advantage in Xcode 3.2.2 and programming for Mac OS (not for iPhone) is a small but nice feature - I do not need to declare the instance variables, even if it is the outlet. Instead I simply declare the property.
For this program, that will fulfill the text field with the formatted date and time, the application delegate class looks so:
#import <Cocoa/Cocoa.h>

@interface TextFieldWithDateTimeAppDelegate : NSObject <NSApplicationDelegate>

@property (assign) IBOutlet NSWindow *window;
@property (retain) IBOutlet NSTextField * text;

- (IBAction)populateText:(id) sender;

@end

And here is the implementation:
#import "TextFieldWithDateTimeAppDelegate.h"

@implementation TextFieldWithDateTimeAppDelegate

@synthesize window;
@synthesize text;

- (IBAction)populateText:(id) sender
{
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDoesRelativeDateFormatting: YES];
[formatter setDateStyle: NSDateFormatterLongStyle];
[formatter setTimeStyle: NSDateFormatterShortStyle];

NSDate *now = [NSDate date];
NSString *formattedDateTime = [formatter stringFromDate: now];

text.stringValue = formattedDateTime;

[formatter release];
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
}

Now, just do not forget to save the changes - Control-S. Few seconds to make the GUI in Interface Builder:

Now "Build and Run":

Saturday, July 3, 2010

Microsoft stops Kin

You are welcome to read yourself:
Microsoft pulls plug on Kin phone
I thought Microsoft had a plan to kill iPhone with an own smartphone - Kin. It looks not bad on photos. I hope the hardware was not bad too. From my point of view, the main problem is the OS installed on these smartphones. So this sentence:
"We are integrating our Kin team with the Windows Phone 7 team, incorporating valuable ideas and technologies from Kin into future Windows Phone releases and beyond."
if it is true, will make a lot of troubles for future Microsoft projects.

Cocoa: How to create buttons without Interface Builder

In Cocoa application it is possible to create controls in the run-time. The application delegate class, automatically generated by Xcode, contains method -applicationDidFinishLaunching. This is a good place to create and initialize such user interface controls.
For example, the following code creates an NSButton object in the left-bottom corner of the application window:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{
NSRect frame = NSMakeRect(10, 40, 90, 40);
NSButton* pushButton = [[NSButton alloc] initWithFrame: frame];
pushButton.bezelStyle = NSRoundedBezelStyle;
[self.window.contentView addSubview: pushButton];

pushButton.target = self;
pushButton.action = @selector(buttonClicked:);

[pushButton release];
}

- (IBAction) buttonClicked: (id)sender
{
NSLog(@"Calling -buttonClicked: with sender: %@", sender);
}

Add method -(IBAction) buttonClicked: (id)sender to the header file of the application delegate, build and launch the application:




In the console you will see the logging message from the -buttonClicked each time you press on the button:

Loading program into debugger…
Program loaded.
run
[Switching to process 22626]
Running…
2010-07-03 10:52:13.424 ButtonWithoutIB[22626:a0f] Calling -buttonClicked: with sender:
2010-07-03 10:52:14.088 ButtonWithoutIB[22626:a0f] Calling -buttonClicked: with sender:
2010-07-03 10:52:15.088 ButtonWithoutIB[22626:a0f] Calling -buttonClicked: with sender:
2010-07-03 10:52:15.288 ButtonWithoutIB[22626:a0f] Calling -buttonClicked: with sender:


In case someone needs, it is possible to press on this button from the code too. Let's send -performClick message to our button from the code. Add this line before the button release:
[pushButton performClick:self];

Now the logging message appears in console when the application starts up:

Same way will work for NSPopUpButton:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{
NSRect frame = NSMakeRect(10, 40, 120, 40);
NSPopUpButton* popUpButton = [[NSPopUpButton alloc] initWithFrame:frame];
[self.window.contentView addSubview:popUpButton];

[popUpButton addItemWithTitle: @"First"];
[popUpButton addItemWithTitle: @"Second"];
[popUpButton addItemWithTitle: @"Third"];

NSLog( @"popUpButton itemArray: %@", popUpButton.itemArray );
NSLog( @"popUpButton itemTitles: %@", popUpButton.itemTitles );

popUpButton.target = self;
popUpButton.action = @selector( itemDidChange: );

[popUpButton release];
}

- (IBAction)itemDidChange:(id)sender
{
NSLog( @"selected item: %@", [sender selectedItem] );
NSLog( @"selected index: %i", [sender indexOfSelectedItem] );
NSLog( @"selected title: %@", [sender titleOfSelectedItem] );
}

The source code in this article is from the nice book about Cocoa: "Cocoa and Objective-C Up and Running".