Een Stap voor Stap Handleiding tot Cocoa-ObjC.

Door Simon Menke.
Pius-X / 4Eco / Ned 2-Mei-2004

Hello World! (Programmeurs traditie)

Inhoud
Inleiding. 1. Nieuw Project. 1.1. Open Xcode. 1.2. Nieuw Project Aanmaken. 2. Het Interface. 2.1. Open MainMenu.nib. 2.2. Het interface opstellen. 2.2.1. Het basis interface opstellen. 2.2.2. Het basis menu opstellen. 2.2.3. Het 'bewerk' interface opstellen. 2.3. Het aan maken van een controller class. 2.4. Het koppelen van de interface elementen. 3. De Code. 3.1. De Header file. 3.2. De Implementation file. 3.2.1. - (void)awakeFromNib 3.2.2. - (IBAction)berekenVanAnaarB:(id)sender 3.2.3. - (IBAction)berekenVanBnaarA:(id)sender 3.2.4. - (IBAction)openBewerkVenster:(id)sender 3.2.5. - (IBAction)sluitBewerkVenster:(id)sender 4. Het Compileren van het programma. 4.1. Mogelijke fouten bij het compileren. Slot. Bibliografie.

Inleiding
Met dit werk over Cocoa-ObjC zult u binnen enkele uren in staat zijn om zelf een programma te schrijven. Dit werk is geschreven in de vorm van een stap voor stap handleiding omdat dit de meest efficiënte manier is om een (programmeer) taal te leren. Ik heb mij enkel toegespitst op een aantal basis handelingen zoals het opbouwen van het ‘user interface’ en het schrijven van Cocoa-ObjC code. Voor dat we daar aan beginnen mat meer informatie over Cocoa-ObjC. Cocoa-ObjC is een programmeertaal (gebaseerd op NeXT ontwikkeld in 1985 voor UNIX) ontwikkeld door Apple voor Mac OS X. Zoals u kunt zien bestaat Cocoa-ObjC uit twee onderdelen: Cocoa, de woordenschat en ObjC, de grammatica. Zo bestaat er ook Cocoa-Java, NeXT, etc deze programmeertalen hebben ofwel de zelfde woordenschat ofwel dezelfde grammatica. Cocoa-ObjC kan enkel op een Mac geschreven en gebruikt worden omdat deze gebaseerd is op onder liggende talen zoals C, C++ en Python. U heeft dus een Mac en de benodigde software nodig (de software: Xcode kunt u gratis downloaden door naar http://developer.apple.com/ te surfen en een nieuwe account aan te maken als u dan ingelogd bent kunt u Xcode v1.2 downloaden). Als u zowel een Mac als Xcode hebt kunt u deze stap voor stap handleiding door lopen.

1.Nieuw Project.
1.1 Open Xcode.
ga naar de map (Fig. 1) Developer/Applications in deze map ziet je XCode staan. Open XCode.

Fig. 1

1.2 Nieuw Project aanmaken.
maak een nieuw project aan door naar het menu 'New Project...'(Fig. 2) te gaan. er zal een nieuw venster open gaan. je ziet nu een lijst met verschillende project types. selecteer 'Cocoa Application' (Fig. 3). klik op de 'Next' knop. nu zie je twee velden verschijnen (Fig. 4) vul in Fig. 2 het eerste veld 'ValueConverter' in. klik nu op de 'Finish' knop. uit eindelijk zie je het 'Project venster'.
Fig. 3 Fig. 4

2.Het Interface
2.1 Open MainMenu.nib.
aan je linker kant zie je een kolom met een aantal 'items' zoals 'Targets' en 'headers'.klik op het 'item' met 'NIB Files' als naam. nu ziet u in de centrale kolom een bestand staan 'MainMenu.nib' genaamd, klik er twee keer op.het programma 'Interface Builder' open zich automatisch.

Fig. 5

2.2 Het interface opstellen.
2.2.1 Het basis interface opstellen.
sleep een nieuw tekstveld naar het venster (Fig. 6) en maak het tekst veld groter door er een keer op te klikken en aan een van de 'drag handels' (de bolletjes) te trekken. sleep nu Fig. 6 een 'PopUp Button' naar het venster. en sleep nu nog en tekstveld naar het venster en maak het ook weer groter. maak het venster nu kleiner zodat er geen overbodige ruimte gebruikt wordt. je venster zou er nu ongeveer zo moeten uit zien. (Fig. 7)

Fig. 7

2.2.2 Het basis menu opstellen.
in het NIB venster zie je een menu icoon met MainMenu als naam dubbel klik er op nu ziet u dit venster. klik één maal op het 'Window' menuitem het submenu opent zich automatisch sleep nu eerst een nieuw menu-item naar het submenu en plaats het helemaal bovenaan klik twee maal op de tekst en verander deze door 'Bewerk Wisselkoersen' (Fig. 9). sleep nu een scheidings menu-item naar het submenu en plaats het op de tweede plaats. (Fig. 8)

Fig. 9

Fig. 8

2.2.3 Het 'bewerk' interface opstellen.

Fig. 9

we maken nu een nieuw venster aan door net als bij de tekst velden een nieuw venster te verslepen (Fig. 9). dubbel klik op het nieuwe venster (het zal zich openen) en sleep er een tabel (Fig. 10) naar toe dan sleep je er drie knopjes naar toe (Fig. 11). en geef ze de namen 'Sluit', 'Verwijder' en 'Nieuw' door op elk knopje twee maal te klikken en de naam in te typen. orden de tabel en de drie knopjes zodat je de afbeelding benadert (Fig. 12).

Fig. 10

Fig. 11 Fig. 12

2.3 Het aan maken van een controller class.
eerst wat meer uit leg. de controller is een object dat er voor zorgt dat alle informatie op de juiste manier behandeld wordt. vb: als je op een knop met de naam (print) Fig. 13 drukt dan gaat die knop aan de controller zegen dat hij het document moet printen. de controller op zijn beurt gaat alle vereiste informatie verzamelen en verwerken en uiteindelijk de informatie door sturen naar de printer. de controller is met andere woorden de dirigent in een (programmatisch) orkest (Fig. 13).

ga naar de 'Classes' tab en scroll helemaal naar links. in de uiterst links kolom ziet u twee elementen staan 'java.lang.Object' en 'NSObject' (Fig. 14). selecteer 'NSObject' en druk op 'return' Interface Builder zal een nieuw element aan maken en het 'MyObject' noemen. noem het nu 'Controller' door er twee maal op te klikken en de nieuwe naam in te typen (Fig. 15). Open nu het info paneel (shift option i). het Info panel open zich. je ziet het volgende. voor dat we verder Fig. 14 gaan moet je zeker zijn dat de Fig. 15 'Controller' class geselecteerd is in de 'Classes' tab, ook is het verstandig om je bestand eens te bewaren. nu dat je bestand bewaart is en de 'Controller' class geselecteerd is kunnen we verder gaan. in het Info paneel klik je op de 'Outlets' tab. hier voeg je de volgende elementen in : 'tekstVeldA', 'tekstVeldB', 'popUpButton', 'bewerkVenster', 'basisVenster' en 'infoBeheerder' door voor elk element op add te klikken en te dubbel klikken op het nieuwe element 'myOutlet' en de naam te veranderen. klik nu op de 'Actions' tab en voer nu net als bij de Outlets de volgende elementen in : 'berekenVanAnaarB', 'berekenVanBnaarA', 'openBewerkVenster' en 'sluitBewerkVenster'.

Nu moet je de Controller instantiëren (Fig. 16) en de bron documenten voor de Controller class aan maken door naar het menu 'Create File for Controller' (Fig. 17) te gaan. er zal een 'sheet' verschijnen hier klik je gewoon op 'Choose'.
Fig. 16

Fig. 17

2.4 Het koppelen van de interface elementen.
in deze stap gaan we aan elk interface element een functie geven en stellen we ze in staat om informatie weer te geven. Elke action stelt een functie voor, elke outlet een gekoppeld interface element en elke binding een informatie 'stroom'. voor dat we de connecties leggen moeten we nog één object toevoegen: de 'infoBeheerder'. sleep een NSArrayController naar het NIB venster (Fig. 18).
Fig. 18

bewaar het document Interface Builder zal je nu vragen in welk formaat hij het document moet bewaren, druk gewoon de Entertoets in. selecteer de NSArrayController (onze infoBeheerder) en open het info paneel. je ziet nu een tabel, voeg twee Fig. 19 nieuwe 'keys' in en noem ze 'naam' en 'wisselkoers' (Fig. . 19).

we beginnen met de actions te koppelen. we openen het basisvenster en klikken één keer op het bovenste tekst veld (laten we het veld A noemen). we houden de ctrl-toets in gedrukt , klikken op het A veld en slepen een 'connectie' naar Controller in het NIB venster (Fig. 20) we laten de muis knop los, in het info paneel zien we nu een lijst met de actions Fig. 20 die we daarnet hadden aan gemaakt. selecteer 'berekenVanAnaarB' end klik op Connect. doe het zelfde voor het tweede tekst veld (veld B) maar deze keer nemen we 'berekenVanBnaarA' als actie. sluit het basis venster en open nu het MainMenu. klik één maal op het 'Window' menu-item en selecteer nu het 'Bewerk Wisselkoersen', sleep een connectie naar Controller en neem deze keer 'openBewerkVenster' als action. sluit MainMenu en open het bewerk venster. klik één maal op de 'Sluit' knop en sleep een connectie naar Controller, selecteer dit maal 'sluitBewerkVenster'. klik een maal op de 'Nieuw' knop en sleep een connectie naar NSArrayController neem 'add' als actie, doe het zelfde voor de 'Verwijder' knop met 'remove' als actie. vervolgens koppelen we de outlets. we open en weer het basis-venster deze keer slepen we een connectie van de Controller naar het A veld en uiteindelijk selecteren we tekstVeldA als outlet. herhaal deze handeling voor de volgende elementen:: element outlet veld B tekstVeldB PopUpButton popUpButton basis-venster basisVenster bewerk-venster bewerkVenster NSArrayController infoBeheerder

en uiteindelijk maken we de bindings. open het bewerk-venster, dubbel klik op de tabel en selecteer de eerste kolom (Fig. 21). ga nu naar het info Fig. 21 paneel en selecteer het bindingspaneel (Fig. 22). klik één maal op de 'value' binding en voer de informatie in zoals op de afbeelding weergegeven (Fig. 23). doe het zelfde voor de tweede kolom maar vul nu van in plaats 'naam' 'wisselkoers' in. sluit het bewerkVenster en open het basisVenster.

Fig. 22

Selecteer het popUpButton in het bindings paneel open je het 'content' element vulde velden in zoals weergegeven op de afbeelding (Fig. 24) open nu het 'contentValues' element en vul de velden weer in zoals op de afbeelding (Fig. 25).

Fig. 23

Fig. 24

Fig. 25

3.De Code.
3.1 De Header file.
O.K. we zijn klaar met het interface open XCode terug en in de linker kolom selecteer je het 'headers' element. in de centrale kolom zie je nu één bestand (Controller.h), klik er twee maal op en een nieuw venster zal zich openen met de volgen de code er in:
/* Controller */ #import <Cocoa/Cocoa.h> @interface Controller : NSObject { IBOutlet id basisVenster; IBOutlet id bewerkVenster; IBOutlet id infoBeheerder; IBOutlet id popUpButton; IBOutlet id tekstVeldA; IBOutlet id tekstVeldB; } - (IBAction)berekenVanAnaarB:(id)sender; - (IBAction)berekenVanBnaarA:(id)sender; - (IBAction)openBewerkVenster:(id)sender; - (IBAction)sluitBewerkVenster:(id)sender; @end

eerst wat uitleg::
@interface Controller : NSObject

op deze regel zien we @interface staan dit betekent dat we een 'interface-block' openen (er is een groot verschil tussen het UI, user-interfase en een classinterface) een class-interface bepaalt het 'uiterlijk' van een class m.a.w. welke functies een ander object een object van deze class kan laten uitvoeren. vervolgens zien we 'Controller' staan dit is de naam van onze class en uiteindelijk zien we ': NSObject' staan dit betekent dat onze class (Controller) alle functionaliteit van de class NSObject erft.

{ IBOutlet IBOutlet IBOutlet IBOutlet IBOutlet IBOutlet } id id id id id id basisVenster; bewerkVenster; infoBeheerder; popUpButton; tekstVeldA; tekstVeldB;

dit is de 'blok' waar alle 'instance variables' bepaalt worden, dit zijn alle variabelen die onze class nodig heeft om zijn taak uit te voeren.
(IBAction)berekenVanAnaarB:(id)sender; (IBAction)berekenVanBnaarA:(id)sender; (IBAction)openBewerkVenster:(id)sender; (IBAction)sluitBewerkVenster:(id)sender;

dit is de blok waar alle functies bepaald worden.
@end

dit betekent dat we onze interface blok sluiten. in dit document moet er maar één aanpassing gemaakt worden. we moeten namelijk een 'NSMutableArray' (een aanpasbare lijst) toevoegen aan de instance-varaiables om al onze wisselkoersen op te slagen. varander
{ IBOutlet IBOutlet IBOutlet IBOutlet IBOutlet IBOutlet } id id id id id id basisVenster; bewerkVenster; infoBeheerder; popUpButton; tekstVeldA; tekstVeldB;

in
{ NSMutableArray *wisselkoersen; IBOutlet IBOutlet IBOutlet IBOutlet IBOutlet IBOutlet } id id id id id id basisVenster; bewerkVenster; infoBeheerder; popUpButton; tekstVeldA; tekstVeldB;

bewaar en sluit het document.

3.2 De Implementation file.
in de linker kolom klik je nu één maal op de 'Implementation Files' folder in de centrale kolom zie je nu twee bestanden staan open het eerste document (Controller.m). het bestand ziet er zo uit:
#import "Controller.h" @implementation Controller - (IBAction)berekenVanAnaarB:(id)sender { } - (IBAction)berekenVanBnaarA:(id)sender { } - (IBAction)openBewerkVenster:(id)sender { } - (IBAction)sluitBewerkVenster:(id)sender { } @end

dit is het bestand waar men de effectieve code schrijft, wat uitleg:
#import "Controller.h"

dit commando importeert het 'header-bestand' dat we eerder bespraken. dit is noodzakelijk omdat de compiler moet weten welke functies en welke variabelen deze class gebruiken.
@implementation Controller

het begin van de 'implementation'-blok voor de class Cotroller. binnen in deze blok worden de functies voor een class geschreven.

- (IBAction)berekenVanAnaarB:(id)sender { } - (IBAction)berekenVanBnaarA:(id)sender { } - (IBAction)openBewerkVenster:(id)sender { } - (IBAction)sluitBewerkVenster:(id)sender { }

dit zijn de vier functie-blokken. elke functie blok begint met '- (IBAction)' de '-' betekent dat het een 'instance-functie' is dit betekent dat men eerst een geïnitialiseerd object van de class Controller moet hebben om de functie te kunnen gebruiken, als er '+' staat heeft men geen 'instance' nodig. '(IBAction)' betekent dat het een user-interface actie terug geeft aan het object dat de functie aanhaalt.
@end

dit is het einde van de 'implementation'-blok voor de class Controller.

3.2.1 - (void)awakeFromNib.
eerst moeten we nog een functie toe voegen. verander:
@implementation Controller - (IBAction)berekenVanAnaarB:(id)sender { } - (IBAction)berekenVanBnaarA:(id)sender { } - (IBAction)openBewerkVenster:(id)sender { } - (IBAction)sluitBewerkVenster:(id)sender { } @end

in:
@implementation Controller - (void)awakeFromNib { } - (IBAction)berekenVanAnaarB:(id)sender { } - (IBAction)berekenVanBnaarA:(id)sender { } - (IBAction)openBewerkVenster:(id)sender { } - (IBAction)sluitBewerkVenster:(id)sender { } @end

de functie '- (void)awakeFromNib' wordt aan gehaald als het programma wordt opgestart en hier gaan we dus de al dan niet bestaande wisselkoersen binnen laden.
type de volgende code: - (void)awakeFromNib { NSArray *tijdelijk = [[NSUserDefaults standardUserDefaults] objectForKey:@"wisselkoersen"]; wisselkoersen = [[NSMutableArray alloc] init]; if (tijdelijk) { NSEnumerator *enume = [tijdelijk objectEnumerator]; NSDictionary *koers; while (koers = [enume nextObject]) { [wisselkoersen addObject:[koers mutableCopy]]; } } [infoBeheerder setContent:wisselkoers]; }

Uitleg: Omdat men geen aanpasbare lijsten kan bewaren worden ze automatisch omgevormd tot gewone lijsten dit zorgt voor het volgende probleem: als we de lijst terug binnen laden is ze niet meer aanpasbaar dus moeten we deze zelf terug aanpasbaar maken. eerst laden we de al dan niet bestaande lijst binnen:
NSArray *tijdelijk = [[NSUserDefaults standardUserDefaults] objectForKey:@"wisselkoersen"];

dan maken we een nieuwe aanpasbare lijst aan:
wisselkoersen = [[NSMutableArray alloc] init];

hier kijken we na of dat de binnen geladen lijst bestaat, de code in deze 'if-blok' wordt dus enkel uitgevoerd als 'tijdelijk' (de binnen geladen lijst) bestaat.
if (tijdelijk) {

dan maken we een 'NSEnumerator' aan dit is een object dat door lijsten loopt en elk element in een lijst terug geeft.
NSEnumerator *enume = [tijdelijk objectEnumerator];

hier maken we een naam aan die we later gebruiken om elk element te apart te gebruiken.
NSDictionary *koers;

nu maken we een 'while-blok' aan dit is een loop met als conditie:(koers = [enume nextObject]) dus de code in deze blok word uitgevoerd zolang 'enume' (de NSEnumerator) een object terug geeft. dit (door enume teruggeven) object wordt 'koers' genoemd.
while (koers = [enume nextObject]) {

dan voegen we de aanpasbare versie van 'koers' toe aan onze aanpasbare lijst (wisselkoersen):
[wisselkoersen addObject:[koers mutableCopy]];

nu sluiten we eerst de 'while-blok' en dan de 'if-blok':
} }

en dan uiteindelijk koppelen we de infoBeheerder aan de wisselkoersen;
[infoBeheerder setContent:wisselkoersen];

3.2.2 - (IBAction)berekenVanAnaarB:(id)sender.
in deze functie maken we de omrekening van het A-Veld naar het B-Veld
- (IBAction)berekenVanAnaarB:(id)sender { }

type in deze blok de volgende code.
- (IBAction)berekenVanAnaarB:(id)sender { int index = [infoBeheerder selectionIndex]; NSDictionary *wisselkoers = [wisselkoersen objectAtIndex: index]; NSString *textOmrekening = [wisselkoers objectForKey: @"wisselkoers"]; float a = [tekstVeldA floatValue]; float omrekening = [textOmrekening floatValue]; float b = (a * omrekening); [tekstVeldB setFloatValue: b]; }

uitleg: eerst vragen we de index van de geselecteerde wisselkoers op:
int index = [infoBeheerder selectionIndex];

dan vragen we de dictionary op die elk naam-wisselkoers paar voorstelt voor de geselecteerde index:
NSDictionary *wisselkoers = [wisselkoersen objectAtIndex: index];

nu vragen we de tekstvoorstelling van de omrekening op:
NSString *textOmrekening = [wisselkoers objectForKey: @"wisselkoers"];

hier vragen we het om te reken bedrag op uit veld-A:
float a = [tekstVeldA floatValue];

en hier transformeren we de tekstvoorstelling van de omrekening om in een floatvoorstelling (getalvoorstelling)
float omrekening = [textOmrekening floatValue];

nu bereken we het nieuwe bedrag:
float b = (a * omrekening);

en uiteindelijk tonen we het nieuwe bedrag:
[tekstVeldB setFloatValue: b];

3.2.3 - (IBAction)berekenVanBnaarA:(id)sender.
in deze functie maken we de omrekening van het B-Veld naar het A-Veld
- (IBAction)berekenVanBnaarA:(id)sender { }

type in deze blok de volgende code.
- (IBAction)berekenVanBnaarA:(id)sender { int index = [infoBeheerder selectionIndex]; NSDictionary *wisselkoers = [wisselkoersen objectAtIndex: index]; NSString *textOmrekening = [wisselkoers objectForKey: @"wisselkoers"]; float b = [tekstVeldB floatValue]; float omrekening = [textOmrekening floatValue]; float a = (b / omrekening); [tekstVeldA setFloatValue: a]; }

uitleg: dit is net het omgekeerde van '- (IBAction)berekenVanAnaarB:(id)sender'.

3.2.4 - (IBAction)openBewerkVenster:(id)sender
in deze functie open we het berwerVenster.
- (IBAction)openBewerkVenster:(id)sender { }

type in deze blok de volgende code.
- (IBAction)openBewerkVenster:(id)sender { [NSApp beginSheet: bewerkVenster modalForWindow: basisVenster modalDelegate: nil didEndSelector: nil contextInfo: nil]; [NSApp runModalForWindow: bewerkVenster]; [NSApp endSheet: bewerkVenster]; [bewerkVenster orderOut: self]; }

uitleg: eerst configureren we het bewerkVenster als een 'sheet' (special venster):
[NSApp beginSheet: modalForWindow: modalDelegate: didEndSelector: contextInfo: bewerkVenster basisVenster nil nil nil];

dan open we het venster:
[NSApp runModalForWindow: bewerkVenster];

en dan sluiten we het venster terug, deze code word pas uitgevoerd als het venster het user-interface sluit commando krijgt:
[NSApp endSheet: bewerkVenster]; [bewerkVenster orderOut: self];

3.2.5 - (IBAction)sluitBewerkVenster:(id)sender.
in deze functie sluiten we het berwerkVenster en we bewaren ook de wisselkoersen omdat ze na elke aanpassing bewaard moeten worden.
- (IBAction)sluitBewerkVenster:(id)sender { }

type in deze blok de volgende code.
- (IBAction)sluitBewerkVenster:(id)sender { [NSApp stopModal]; [[NSUserDefaults standardUserDefaults] setObject:wisselkoersen forKey:@"wisselkoersen"]; }

uitleg: eerst geven we het venster het sluit commando.
[NSApp stopModal];

dan bewaren we de wisselkoersen in de voorkeuren van het programma:
[[NSUserDefaults standardUserDefaults] setObject:wisselkoersen forKey:@"wisselkoersen"];

en dan uiteindelijk koppelen we de infoBeheerder weer aan de wisselkoersen zo dat het interface geüpdate wordt.
[infoBeheerder setContent:wisselkoersen];

4.Het Compileren van het programma.
in het project-venster zie je een knop met een hamertje en een 'play-knopje'. klik er een maal op om het programma te compileren en het daarna op te starten.

4.1 Mogelijke fouten bij het compileren.
er is één mogelijke fout die de compiler kan terug geven tijdens het compileren, namelijk 'passing arg 2 of 'NSApplicationMain' from incompatible pointer type'. deze fout can zeer snel op gelost worden door in het 'main.m' document 'int main(int argc, char *argv[])' door 'int main(int argc, const char *argv[])' te veranderen. (het 'main.m' document kan men vinden in de 'Implementation Files' folder)

Slot.
Zoals u hebt gezien kan men zeer snel een simpel programma schrijven dat in staat is om een aantal basis functies uit te voeren. Een herhaling eerst hebben we een nieuw project aangemaakt vervolgens hebben we het user-interfase opgebouwd dan hebben we de code geschreven en uiteindelijk hebben we het programma gecompileerd. Ik heb met opzet het programma niet in zijn optimale vorm maakt zodat u hier zelf verbeteringen kan doorvoeren. Vanaf het moment waarop u het programma optimaal vindt kunt u verder doorstromen naar meer gecompliceerde handleidingen. Deze handleidingen kunt u vinden op alle sites in de bibliografie.

Bibliografie.
Apple :: CocoaDev :: CocoaDevCentral :: Mamasam :: http://developer.apple.com/ http://www.cocoadev.com/ http://www.cocoadevcentral.com/ http://cocoa.mamasam.com/