45
Introduction to Cocoa Programming for Mac OS X Arthur Clemens 27 May 2008 - Amsterdam

Introduction To Cocoa

Embed Size (px)

DESCRIPTION

This tutorial walks you through the fundamentals of Cocoa: XCode Tools, Interface Builder, Objective-C (variables, methods and memory management), and goes on to explain useful Cocoa principles for building an Image Resizer application: how to open and process files (using both a dialog screen and drag and drop), NSImage, messaging and saving. Get the source code at http://visiblearea.com/blog/bin/view/VisibleArea/Image_Resizer_a_Cocoa_Tutorial

Citation preview

Page 1: Introduction To Cocoa

Introduction to CocoaProgramming for Mac OS X

Arthur Clemens

27 May 2008 - Amsterdam

Page 2: Introduction To Cocoa

What are we going to make?

What we are going to create

Page 3: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 3

What will you learn?

XCode Tools

Interface Builder

Objective-C

How to open and process files:open dialog, drag on icon, drag on app

Extending classes

Image resizing

Messaging

Saving

Page 4: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 4

http://developer.apple.com/

XCode Tools

Page 5: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 5

New application

Page 6: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 6

XCode interface

Page 7: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 7

Interface builder

Page 8: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 8

Drag and drop the interface

Page 9: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 9

Create AppController class

“AppController”

Page 10: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 10

Instantiate AppController

Page 11: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 11

Adding outlets

Page 12: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 12

Adding actions

Page 13: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 13

Setting properties

Page 14: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 14

Make connections

Page 15: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 15

Creating files

ctrl-drag

Page 16: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 16

Testing UI output

AppController.m

- (IBAction)exportImage:(id)sender{

NSLog(@"export:%@", sender);}

- (IBAction)updateSliderValue:(id)sender{

NSLog(@"updateSliderValue:%f", [sender floatValue]);}

Page 17: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 17

Availability of UI objects

AppController.m

- (id)init{

self = [super init];if (self) {

NSLog(@"exportButton=%@", exportButton); } return self;}

- (void)awakeFromNib{

NSLog(@"exportButton=%@", exportButton);}

Page 18: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 18

Selectors: setting the size textfield

AppController.m

- (void)awakeFromNib{

[imageSizeText setStringValue:@""];}

- (IBAction)updateSliderValue:(id)sender{

[imageSizeText setStringValue:[sender stringValue]];}

Page 19: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 19

Objective-C is C with extensions

object instance:

[NSMutableArray alloc]

pointer to object:

NSMutableArray* list;list = [NSMutableArray alloc];

initialize object:

NSMutableArray* list;list = [NSMutableArray alloc];[list init];

shorthand:

NSMutableArray* list = [[NSMutableArray alloc] init];

destroy:

[list release];

Page 20: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 20

Methods

method that takes an argument:

selector = addObject:

[list addObject:foo];

multiple arguments:

[list insertObject:foo atIndex:5];

selector = insertObject:atIndex:

return values:

int x = [list count];

Page 21: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 21

Memory management with reference counting

-alloc allocates memory and returns object with retain count of 1at 0 the object is deallocated and the memory freed

-copy makes a copy of an object, and returns it with retain count of 1

-retain retain count ++

-release retain count --retain count = number of references to the object

-autorelease decreases the reference count of an object by 1 at some stage in the future

NSMutableArray* list = [[[NSMutableArray alloc] init] autorelease];

or:[NSNumber numberWithInt:5];

convenience constructor, see:+ (NSNumber *)numberWithInt:(int)value

Cocoa class methods return autoreleased objects

Page 22: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 22

Member variables: storing the image file path

AppController.h

@interface AppController : NSObject{ IBOutlet NSTextField *imageSizeText; IBOutlet NSImageView *imageView; IBOutlet NSButton *exportButton; IBOutlet NSSlider *slider;

NSString* imageFilePath;}- (void)setImageFilePath:(NSString*)newFilePath;

AppController.m

- (void)setImageFilePath:(NSString*)newFilePath{

[newFilePath retain];[imageFilePath release];imageFilePath = newFilePath;

}- (NSString*)imageFilePath{

return imageFilePath;}- (void)dealloc { [self setImageFilePath:nil]; [super dealloc];}

Very simple rules for memory management in Cocoahttp://www.stepwise.com/Articles/Technical/2001-03-11.01.html

Page 23: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 23

Opening an image file in a dialog window

AppController.m

- (IBAction)showOpenPanel:(id)sender{ NSOpenPanel *op = [NSOpenPanel openPanel]; [op beginSheetForDirectory:nil file:nil types:[NSImage imageFileTypes] modalForWindow:[NSApp mainWindow] modalDelegate:self didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:nil]; }- (void)openPanelDidEnd:(NSOpenPanel *)op returnCode:(int)returnCode contextInfo:(void *)ci{ if (returnCode != NSOKButton) return; NSString *path = [op filename];}

AppController.h

- (IBAction)showOpenPanel:(id)sender; ... and update the NIB file

only PNG:[NSArray arrayWithObject:@"png"]PNG and GIF:[NSArray arrayWithObjects:@"png",@"gif",nil]

Page 24: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 24

Connecting the menu in IB

Page 25: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 25

Putting the image into the view

AppController.m

- (BOOL)importImageFromPath:(NSString*)path{

NSImage *image = [[[NSImage alloc] initWithContentsOfFile:path] autorelease];if (!image) return NO;[imageView setImage:image];return YES;

}

Update:

- (void)openPanelDidEnd:(NSOpenPanel *)op returnCode:(int)returnCode contextInfo:(void *)ci{ if (returnCode != NSOKButton) return; NSString *path = [op filename];

[self importImageFromPath:path];[self setImageFilePath:path];

}

Page 26: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 26

Opening the image file when dragging on app icon

Step 1: add code

AppController.m

- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)path{

// do a check on filetype here...BOOL result = [self importImageFromPath:path];if (!result) return NO;[self setImageFilePath:path];return YES;

}

Step 2: edit app document types...

Step 3: set the File’s Owner delegate...

Page 27: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 27

Opening target info

Command-i

Page 28: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 28

Set the app delegate in IB

Page 29: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 29

Drag and drop onto the app window

We need to extend the image view.

Step 1: create a new subclass for NSImageView: ImageView

Step 2: edit the code

@interface ImageView : NSImageView{}

Step 3: drag new .h file onto IB and set view to new subclass...

Step 4: add drag and drop methods...

Page 30: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 30

Set image view to new subclass in IB

Page 31: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 31

Drag and drop methods

ImageView.m

@implementation ImageView

#pragma mark Drag and drop

- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender{ return NSDragOperationLink;}- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender{ return YES;}- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender{ return YES;}- (void)concludeDragOperation:(id <NSDraggingInfo>)sender{ [super concludeDragOperation:sender];}

@end

Page 32: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 32

Testing: loading an image from the app bundle

Drag image on XCode: Resources

AppController.m

- (void)awakeFromNib{ [imageSizeText setStringValue:@""]; NSString* path = [[NSBundle mainBundle] pathForImageResource:@"anteprima.com.png"]; [self importImageFromPath:path];}

Page 33: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 33

Show image in real size

Override NSImageView’s drawRect:

ImageView.m

- (void)drawRect:(NSRect)rect{ if ([self image]) { NSRect imageRect; imageRect.origin = NSZeroPoint; imageRect.size = [[self image] size]; NSRect drawRect = imageRect; [[self image] drawInRect:drawRect fromRect:imageRect operation:NSCompositeSourceOver fraction:1.0]; }}

The image is now oriented bottom left.

Page 34: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 34

Orient to top left

Flip coordinate system:

ImageView.m

- (BOOL)isFlipped{ return YES;}

Page 35: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 35

Use a transformation to flip the image

Update drawRect in ImageView.m

- (void)drawRect:(NSRect)rect{ if ([self image]) { NSRect imageRect; imageRect.origin = NSZeroPoint; imageRect.size = [[self image] size]; NSAffineTransform* transform = [NSAffineTransform transform]; [transform translateXBy:0.0 yBy:1.0 * imageRect.size.height]; [transform scaleXBy:[zoomFactor floatValue] yBy:-[zoomFactor floatValue]]; [transform concat]; NSRect drawRect = imageRect; [[self image] drawInRect:drawRect fromRect:imageRect operation:NSCompositeSourceOver fraction:1.0]; }}

Page 36: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 36

Store zoom factor in image view

ImageView.h

@interface ImageView : NSImageView { NSNumber* zoomFactor;}

ImageView.m

- (void)setZoomFactor:(NSNumber*)newZoomFactor{ [newZoomFactor retain]; [zoomFactor release]; zoomFactor = newZoomFactor; [self setNeedsDisplay:YES];}- (void)dealloc{ [self setZoomFactor:nil]; [super dealloc];}- (id)initWithFrame:(NSRect)frame { if (![super initWithFrame:frame]) { return nil; } [self setZoomFactor:[NSNumber numberWithFloat:1]]; return self;}

Page 37: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 37

Connect the slider value to the image size

Replace updateSliderValue in AppController.m

- (IBAction)updateSliderValue:(id)sender{ [imageView setZoomFactor:[sender objectValue]];}

Update drawRect in ImageView.m

[transform translateXBy:0.0 yBy:[zoomFactor floatValue] * imageRect.size.height];

Page 38: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 38

Getting the image size

Using NSNotification messages

ImageView.m

- (void)notifyImageUpdated{ [[NSNotificationCenter defaultCenter] postNotificationName:@"imageUpdated" object:[self imageSize]];}- (NSValue*)imageSize{ NSSize imageSize = [[self image] size]; imageSize.width *= [zoomFactor floatValue]; imageSize.height *= [zoomFactor floatValue]; return [NSValue valueWithSize:imageSize];}

Add to setZoomFactor:

[self notifyImageUpdated];

Page 39: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 39

Receiving the notification

Add to awakeFromNib in AppController.m

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleImageUpdated:) name:@"imageUpdated" object:nil];

Unsubscribe before AppController is destroyed:

- (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; [self setImageFilePath:nil]; [super dealloc];}

Update the text field

- (void)handleImageUpdated:(NSNotification*)note{ NSSize size = [[note object] sizeValue]; [imageSizeText setStringValue:[NSString stringWithFormat:@"%.0f x %.0f", size.width, size.height]];}

Page 40: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 40

Update the text field when the image is loaded

Override to setImage in ImageView.m

- (void)setImage:(NSImage *)image{ [super setImage:image]; [self notifyImageUpdated];}

Page 41: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 41

Update the file path when the image is dragged

Update concludeDragOperation in ImageView.m

- (void)concludeDragOperation:(id <NSDraggingInfo>)sender{ [super concludeDragOperation:sender]; NSPasteboard *pboard = [sender draggingPasteboard]; NSString* draggedFilePath = nil; if ( [[pboard types] containsObject:NSFilenamesPboardType] ) { NSArray *files = [pboard propertyListForType:NSFilenamesPboardType]; draggedFilePath = [files objectAtIndex:0]; } [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh]; [[NSNotificationCenter defaultCenter] postNotificationName:@"imageDragged" object:draggedFilePath];}

Register for notifications in AppController.m

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleImageDragged:) name:@"imageDragged" object:nil];

- (void)handleImageDragged:(NSNotification*)note{ NSString* imagePath = [note object]; [self setImageFilePath:imagePath];}

Page 42: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 42

Export the image: Categories

Categories are the prototypes of Cocoa.

Extend a class without subclassing

PrettyPrintCategory.h

@interface NSArray (PrettyPrintElements)- (NSString *)prettyPrintDescription;@end

PrettyPrintCategory.m

#import "PrettyPrintCategory.h"

@implementation NSArray (PrettyPrintElements)- (NSString *)prettyPrintDescription { // implementation code here...}

@end

Create new Cocoa class ImageViewAdditions (m and h files)

Page 43: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 43

A category on NSImageView

ImageViewAdditions.h

@interface NSImageView (ImageViewAdditions)- (NSImage*)imageOfSize:(NSSize)size;@end

ImageViewAdditions.m

@implementation NSImageView (ImageViewAdditions)

- (NSImage*)imageOfSize:(NSSize)size{

NSImage* sourceImage = [self image];NSRect scaledRect;scaledRect.origin = NSZeroPoint;scaledRect.size.width = size.width;scaledRect.size.height = size.height;NSImage* copyOfImage = [[[NSImage alloc] initWithSize:size] autorelease];[copyOfImage lockFocus];[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];[sourceImage drawInRect:scaledRect

fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];

[copyOfImage unlockFocus];return copyOfImage;

}

@end

Page 44: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 44

Export the image

In AppController.m:

- (IBAction)exportImage:(id)sender{ NSSize size = [[imageView imageSize] sizeValue]; NSString* path = [self createFilePath:imageFilePath size:size]; NSImage* imageCopy = [imageView imageOfSize:size]; [self saveImage:imageCopy toPath:path];}

- (NSString*)createFilePath:(NSString*)imageFilePath size:(NSSize)size{ NSString *pathExtension = [imageFilePath pathExtension]; NSString* barePath = [imageFilePath stringByDeletingPathExtension]; return [NSString stringWithFormat:@"%@_%.0fx%.0f.png", barePath, size.width, size.height];}

- (BOOL)saveImage:(NSImage*)image toPath:(NSString*)path{

NSData *imageAsData = [image TIFFRepresentation];NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageAsData];NSData* fileData = [imageRep representationUsingType:NSPNGFileType properties:nil];NSString* filePath = [path stringByExpandingTildeInPath];BOOL success = [fileData writeToFile:[path stringByExpandingTildeInPath] atomically:YES];return YES;

}

Page 45: Introduction To Cocoa

27 May 2008 | Introduction to Cocoa | 45

Result