C Cocoa GUI

raumgleita

Cadet 4th Year
Registriert
Feb. 2008
Beiträge
119
Hallo zusammen!

Ich bin Cocoa/Obj. C Neuling und suche vergeblich wie ich ein GUI mit Code erstellen kann. Alle bisher gefundenen Tutorials zeigen das verwenden des Interface Builders. Ich möchte Labels mit Dateinamen zur Laufzeit erstellen. Ich kann mit einer NSView Klasse zeichnen docht keine Formular Elemente generieren.
Mein Ziel: Samba Share mit Software Packages auslesen und diese auf einem GUI darstellen. Checkboxen zur Auswahl von Software Paketen und die Installation mit einem AppleScript durchführen.

Besten Dank für jeglichen Hinweis
raumgleita
 
Vielleicht findest du auf der Seite "http://cocoadevcentral.com/" etwas passendes. Ich lese mich derzeit auch in Objective-C ein und finde die Sprache eigentlich von der Syntax her grässlich xD, außerdem würd ich gern wissen wie die Verknüpfungen zwischen dem Interface Builder und dem eigentlichen Code funktionieren, das ist einfach unverständlich finde ich.
 
finde die Sprache eigentlich von der Syntax her grässlich
Ebenfalls! Ich komme von Java und der Syntax ist für mich grauenhaft.

Obj. C ein Hello World Label
Code:
    NSString* hello = @"Hello, World!";
    NSPoint point = NSMakePoint(15, 75);
    NSMutableDictionary* font_attributes = [NSMutableDictionary new];
    NSFont* font = [NSFont fontWithName:@"Futura-MediumItalic" size:42];
    [font_attributes setObject:font forKey:NSFontAttributeName];
    [hello drawAtPoint:point withAttributes:font_attributes];
    [font_attributes release];

Java
Code:
JLabel label1 = new JLabel("Hello World?", JLabel.CENTER);
title.setFont(new Font("Serif", Font.BOLD, 48));
content.add(label1);

Das ist fast schon absurd. Vielleicht auch nur als Neuling....

außerdem würd ich gern wissen wie die Verknüpfungen zwischen dem Interface Builder und dem eigentlichen Code funktionieren, das ist einfach unverständlich finde ich.
Ich habs nicht gross probiert doch scheint mir diese Anleitung ziemlich verständlich:
http://developer.apple.com/mac/library/documentation/cocoa/conceptual/ObjCTutorial/06Controller/06Controller.html
Sonst gibt es noch youtube Videos welche die Verbindung zeigen.
 
Ich komme auch von Java^^

Ich find gut dass es irgendwie komplett auf MVC aufbaut. Aber ich versteh zum Beispiel nicht, wie er von der main.m in der die App ja gestartet wird dann zu meiner XYZDocument.m kommt. Das ist einfach unverständlich für mich und ich kann das aus dem Code nicht herauslesen. Bin aber auch erst seit ein paar Tagen dabei das zu lernen, und meist anhand des x02100 Podcasts (XCode von Null auf Hundert)...

Danke für den Link, werd ich mir mal anschaun.
 
raumgleita schrieb:
Mein Ziel: Samba Share mit Software Packages auslesen und diese auf einem GUI darstellen. Checkboxen zur Auswahl von Software Paketen und die Installation mit einem AppleScript durchführen.
Klingt als suchst du eine Tabelle(NSTableView). Die kannst du dann einfach an eine Liste mit deinen Dateinamen binden(Stichwort: Cocoa Bindings). Ich kann dir nur wärmstens http://cocoadevcentral.com/ empfehlen, davon speziell http://cocoadevcentral.com/articles/000080.php

raumgleita schrieb:
Obj. C ein Hello World Label
Ein „Hello World Label“ erreicht man dadurch, dass man im Interface-Builder ein Label von der Palette aufs Fenster zieht. Kein Mensch erstellt die selbst, wenn er nicht muss. Und ich bin mir ziemlich sicher, dass es für dein spezifisches Problem sicherlich eine Lösung gibt, bei dem du keinen GUI-Code anfassen musst.

Abgesehen ließe sich dein Hallo-Welt-Beispiel auch wesentlich einfacher gestalten(für ein Hello-World muss man nicht umständlich irgendwelche Schriften verändern.).
Code:
[@"Hello, World!" drawAtPoint: NSMakePoint(15, 75) withAttributes: [NSDictionary dictionary]]
Allerdings ist dieses Beispiel nicht äquivalent zu deinem Java-Bespiel, weil du so kein „echtes“ Label zeichnest sondern einfach nur einen Text auf die View, die zufällig gerade im Fokus ist.
 
Zuletzt bearbeitet:
Hmm... Ich habe das genannte Beispiel heruntergeladen und die Bindings durchgeführt. Leiders startet die App jetzt nicht mehr. Habs zwei mal komplett neu gemacht mit dem selben Resultat.
Es heisst Build Succeeded aber ich hab folgende Meldung in der gdb Console:
Code:
2010-05-05 13:45:50.252 MailDemo[3206:a0f] An uncaught exception was raised
2010-05-05 13:45:50.255 MailDemo[3206:a0f] Cannot create BOOL from object (
) of class NSCFArray
2010-05-05 13:45:50.257 MailDemo[3206:a0f] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Cannot create BOOL from object (
) of class NSCFArray'
*** Call stack at first throw:
(
	0   CoreFoundation                      0x987d240a __raiseError + 410
	1   libobjc.A.dylib                     0x90c2e509 objc_exception_throw + 56
	2   CoreFoundation                      0x987d2138 +[NSException raise:format:arguments:] + 136
	3   AppKit                              0x9534138a _NSHandleBindingException + 108
	4   AppKit                              0x94d8572e _NSBoolFromValue + 490
	5   AppKit                              0x94d8505c -[NSEditableBinder _setStatesImmediatelyInObject:mode:triggerRedisplay:] + 166
	6   AppKit                              0x94d84faf -[NSEditableBinder _observeValueForKeyPath:ofObject:context:] + 124
	7   AppKit                              0x94ccc9f9 -[NSBinder _performConnectionEstablishedRefresh] + 85
	8   AppKit                              0x94cbf104 -[NSObject(NSKeyValueBindingCreation) bind:toObject:withKeyPath:options:] + 721
	9   AppKit                              0x94d6b067 -[NSNibBindingConnector establishConnection] + 156
	10  AppKit                              0x94c9a33b -[NSIBObjectData nibInstantiateWithOwner:topLevelObjects:] + 1249
	11  AppKit                              0x94c98450 loadNib + 257
	12  AppKit                              0x94c97848 +[NSBundle(NSNibLoading) _loadNibFile:nameTable:withZone:ownerBundle:] + 228
	13  AppKit                              0x94c97759 +[NSBundle(NSNibLoading) loadNibFile:externalNameTable:withZone:] + 158
	14  AppKit                              0x94c976a4 +[NSBundle(NSNibLoading) loadNibNamed:owner:] + 383
	15  AppKit                              0x94c944a9 NSApplicationMain + 434
	16  MailDemo                            0x0000268c main + 30
	17  MailDemo                            0x00002665 start + 53
)
sharedlibrary apply-load-rules all
(gdb)

Ich hab den Code nicht verändert sondern nur die Bindings im IB gemacht. Anscheinend will das Programm an einer Stelle ein Bool Objekt vom NSMutableArray machen...

Hab das Projekt auf rs hochgeladen. DL

Gruss
raumgleita
 
Ja, du hast ja auch mehrfach "Hidden" und "Availability" etc. (alles Booleans) an irgendwelche Collections gebunden. Du musst bei so etwas die „value“ an das Array binden. Und notfalls mehrmals auf das Control draufklicken, damit du auch bei der NSTableColumn landest und nicht der ScrollView. Oben im Inspector steht wo du gerade bist. Achte genau drauf, was auf den Screenshots abgebildet ist.

Auch würde ich dir dringend empfehlen, das alles nach und nach zu machen und zu gucken obs noch läuft und nicht alles auf einmal, dann darfst du nämlich schön Fehler suchen.
 
Zuletzt bearbeitet:
Danke! Hat funktioniert. Leiders ist mir das Beispiel immernoch zu komplex um selber etwas zu versuchen.

Ich habe nun mal eine Frage. Wie kann ich eine Tableview im voraus mit einem Array füllen? Ich habe gesehen, dass man Objekte mit vordefinierten Daten in der init Methode setzten kann. Wie kann ich einen vordefinierten Array benutzen? Die init Methode ist ja nur der Konstruktor, der ausgeführt wird, wenn ein neues Objekt erstellt wird.

Ok konkret. Ich habe nun eine Klasse App die beim Klick eines Buttons einen neuen Eintrag "Firefox" in einer NSTableView erstellt:
Code:
@implementation App

-(id)init{
	[super init];
	[self setName: @"Firefox"];
	return self;
}

-(NSArray*)name{
	return name;
}

-(void)setName:(NSArray*)aName{
	name = [NSArray copy];
	[name release];
	name = aName;
}

-(void)dealloc{
	[name dealloc];
	[super dealloc];
}
@end

Nun möchte ich das der Array "name" mit den Dateinamen des Application Folders gefüllt wird:
Code:
array = [[NSFileManager defaultManager]contentsAtPath:@"/Applications/" error:nil];
Wie und wo überschreib ich den "name" Array?

Gruss
raumgleita
 
Zuletzt bearbeitet:
raumgleita schrieb:
Code:
-(void)setName:(NSArray*)aName{
	name = [NSArray copy];
	[name release];
	name = aName;
}
Tödlich. Erstens macht das keinen Sinn(Objekt erstellen um es dann gleich wieder freizugeben). Zweitens hast du daduch ein Speicherleck weil du die ursprüngliche Referenz gelöscht hast und die so nie wieder freigeben kannst. Und drittens hast du an aName nicht retain o.Ä. gesendet um zu verhindern dass das evtl gleich wieder gelöscht wird. In http://www.cocoadevcentral.com/d/learn_objectivec/ steht auch wie es besser geht:
Code:
-(void)setName:(NSArray*)aName{
	[name autorelease];
	name = [aName retain];
}
Alternativ kannst du auch die ObjC 2.0 Properties nehmen, da wird der entsprechende Code automatisch generiert.

Wie und wo überschreib ich den "name" Array?
Am besten in -awakeFromNib. Und beachte dabeiKVO und KVC(der ganze setValue:ForKey:, didChangeValue:ForKey:-Kram). Wenn du nämlich einfach irgendwo Werte veränderst kriegt deine GUI davon nichts mit, weil sie auf entsprechende Events wartet. Properties können dir da auch Arbeit abnehmen. Da reicht dann ein einfaches
Code:
self.name = …

In init geht es natürlich auch, aber mach dein init lieber so wie in http://www.cocoadevcentral.com/d/learn_objectivec/, [super init] gibt einen Wert zurück und den sollte man auch benutzen.
 
Zuletzt bearbeitet:
Danke für deine Hilfe! Auf cb bist du wahrscheinlich einer der wenigen Cocoa Programmierer.

So ich meine Klasse nun versucht zu erweitern:
Code:
#import "App.h"

@implementation App

-(void)awakeFromNib{
	NSLog(@"awakeFromNib");
	App * list = [[App alloc]init];
	[self willChangeValueForKey:(@"title")];
	list.title = @"Safari";
	[self didChangeValueForKey:(@"title")];
}

-(id)init{
	if ( self = [super init] ) { 
		self.title = @"Chrome";
		NSLog(@"INIT");
	} 
	return self; 
}

//observer
- (void)registerAsObserver
{
    // register "inspector" to receive change notifications
	
    // for the "title" property of the "App" object
	
    // and that both the old and new values of "title"
	
    // should be provided to the observer

    [list addObserver:inspector
	 
			  forKeyPath:@"list.title"
	 
                 options:(NSKeyValueObservingOptionNew |
						  
						  NSKeyValueObservingOptionOld)
	 
				 context:NULL];
		NSLog(@"observer registered");
}

//observable
- (void)observeValueForKeyPath:(NSString *)keyPath

					  ofObject:(id)object

                        change:(NSDictionary *)change

                       context:(void *)context

{	
	NSLog(@"observer notification triggered");
    if ([keyPath isEqual:@"list.title"]) {
		[tableView1 reloadData];
		NSLog(@"reload data in tableview1");
    }
	
    // be sure to call the super implementation
	
    // if the superclass implements it
	
    [super observeValueForKeyPath:keyPath
	 
						 ofObject:object
	 
						   change:change
	 
						  context:context];
	
}

@synthesize title;

-(void)dealloc{
	[title release];
	[super dealloc];
}

@end

Leiders wird bis auf die init Funktion keine weitere ausgeführt. Überanschenderweise nicht mal die awakefromNib Funktion. Was mache ich nur falsch?
Der Code ist vermutlich Blödsinn doch zu meiner Verteidigung muss man sagen, dass ich ihn noch nicht mal testen konnte ;).

Die Idee (nach meinem Verständnis):
  1. AddObserver
  2. willChangeValue...
  3. AppListe in Array speichern (welcher mit der tableview verbunden ist)
  4. didChangeValue
  5. observeValueForKeyPath: reload data

So sollte die Reihenfolge aussehen, richtig?

Nebenbei:
Code:
self.title = @"Chrome";
Warum kann ich ein String Objekt so in ein Array speichern? Gibt zwar eine Warnung aber warum funktioniert das überhaupt?

Gruss
raumgleita
 
Nein, das Problem ist, dass du App überhaupt nirgends einbindest. Es gibt nicht den geringsten Grund, warum da irgendwas aufgerufen werden sollte. Kopier das mal alles man in die Delegate-Klasse. Oder lieber nicht. Das meiste was da steht ist ziemlich überflüssig. Der ganze Code zeugt von mangelndem Verständnis was du da eigentlich machst. Klingt vielleicht hart, aber ist so.
 
Zurück
Oben