2009-04-30 18 views
3

Ich habe versucht, uns CALayers als "Sprites" in einer iPhone-Anwendung, an der ich arbeite, zu arbeiten. Nach dem, was ich gelesen habe, scheint das hier das Richtige zu sein.Gibt es einen Grund, warum ich einen CALayer nicht außerhalb einer UIView-Klasse initialisieren kann?

Wenn ich die Ebene innerhalb einer UIView-Klasse eingerichtet habe, kann ich die Ebene auf dem Bildschirm anzeigen lassen: NSLog (@ "GameView initWithCoder");

NSString* imagePath = [[NSBundle mainBundle] pathForResource:@"earth" ofType:@"png"]; 
earthImage = [[UIImage alloc] initWithContentsOfFile:imagePath]; 

earthLayer = [CALayer layer]; 
[earthLayer setContents:(id)[earthImage CGImage]]; 
[earthLayer setBounds:CGRectMake(0, 0, 32, 32)]; 
[earthLayer setPosition:CGPointMake(50, 50)]; 
[earthLayer setName:@"earth"]; 

[[self layer] addSublayer:earthLayer]; 

Allerdings, wenn ich die CALayer außerhalb des UIView zu initialisieren versuchen, meine Anwendung abstürzt:

- (void)loadImage:(NSString*)fileName 
{ 
    NSLog(@"GamePiece loadImage"); 
    NSArray* fileNameParts = [fileName componentsSeparatedByString:@"."]; 
    imageName = [[fileNameParts objectAtIndex:0] retain]; 
    NSString* ext = [fileNameParts objectAtIndex:1]; 

    NSString* imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:ext]; 
    image = [[UIImage alloc] initWithContentsOfFile:imagePath]; 

    layer = [CALayer layer]; 
    [layer setContents:(id)[image CGImage]]; 

    [layer setBounds:CGRectMake(0.0f, 0.0f, 32.0f, 32.0f)]; 
    [layer setPosition:CGPointMake(50.0f, 50.0f)]; 
    [layer setName:imageName]; 

    [fileNameParts release], fileNameParts = nil; 
    [ext release], ext = nil; 
} 

NSLog(@"GameView initWithCoder"); 
earth = [[GamePiece alloc] init]; 
[earth loadImage:@"earth.png"]; 

[[self layer] addSublayer:[earth layer]]; 

Klar bin ich etwas falsch hier zu tun, aber ich nur don Ich weiß nicht, was das sein würde. Ich habe den Core Animation Programming Guide und auch das iPhone Application Programming Guide gelesen, aber sie scheinen wirklich nicht darüber zu sprechen, wie man einen CALayer anders als im Vorbeigehen initialisiert.

Ich brauche nur ein sanftes Anstupsen in die richtige Richtung. Ich habe ein ziemlich gutes Verständnis über alles, was ich gelernt habe, wenn es darum geht, das iPhone zu programmieren. Ich habe nur ein paar Probleme mit Grafiken, Layern und Animationen.

Vielen Dank im Voraus!

Antwort

2

Sie besitzen nicht die Ebene, die Sie von der layer Methode erhalten, also müssen Sie Besitz übernehmen. Irgendwann, nachdem Ihre Methode zurückgegeben wurde, wird die Zuordnung aufgehoben und der Absturz verursacht. Zur Heilung dieser:

layer = [[CALayer layer] retain]; // now we explicitly take ownership of this layer 

Auch vergessen Sie nicht, die Schicht zu lösen, da Sie es besitzen jetzt:

// in your dealloc method 
[layer release]; 

Sie sollten die Memory Management Programming Guide for Cocoa um weitere Details zu überprüfen, wenn Sie eigene Objekte und was Ihre Verantwortlichkeiten betreffen das Speichermanagement.

0

Jason, vielen Dank für die Antwort! Ihre Antwort scheint jedoch keinen Einfluss auf das Ergebnis meines Problems zu haben.

Eigentlich ist die Antwort "ist" etwas im Zusammenhang mit Speicherverwaltung. Nicht der Layer, sondern der Name, den ich vom NSArray nach dem Aufruf von componentsSeparatedByString: erhalten habe. Wenn ich das NSArray nicht freigebe, funktioniert der Code gut. Also muss ich eine Kopie des NSString im Array erstellen, anstatt das Objekt im Array zu verwenden.

Ich muss sagen, dass ich die Memory Management Programming Guide für Cocoa nicht gelesen habe, wie Sie vorschlagen. Ich habe jedoch mehrere andere Bücher gelesen, in denen über den in Cocoa verwendeten Standard für erstellte Objekte und ihre Anzahl an Retainern gesprochen wurde. Ich habe ein faires Verständnis, aber anscheinend nicht so gut wie nötig.

Ich hatte wirklich einen Verdacht, dass ich die Zeichenfolge aus dem Array kopieren sollte, aber als es in der UIView funktionierte, nahm ich einfach an, dass es richtig war. Problem war, ich habe es nicht so behalten, als wäre ich in meinem GamePiece-Objekt.

Danke, dass Sie mich wenigstens in die richtige Richtung sehen! Mit diesem Code war ich wirklich am Ende.

+1

Eigentlich ist die Schicht immer noch ein Problem, auch wenn Sie bisher Glück gehabt haben. Sie müssen es behalten, wenn es ein Ivar ist. Was den Dateinamen betrifft, habe ich das übersehen.Sie müssen das nicht wirklich behalten oder kopieren. Einfach nicht freigeben :) Grundsätzlich müssen Dinge, die Sie nur während der Dauer Ihrer Funktion verwenden, nicht aktiv verwaltet werden, es sei denn, Sie haben sie erstellt (alloc/copy/new). Dinge, die Sie beibehalten möchten, müssen aktiv verwaltet werden (wenn Sie also nicht zugewiesen/kopiert/neu sind, müssen Sie sie behalten). –

0

Um zu tun, was Sie wollten, hier ist, wie Sie Ihren Code ändern können.In loadImage: Verwendung hinzufügen diese Linie den UIImage Sie in die earth Schicht geladen:

self.enthaelt = image.CGImage;

CALayers haben eine contents Eigenschaft, die Sie zu einem CGImage anstatt sich durch die Mühe der Verwendung von Core Graphics ziehen ruft das Bild in die Ebene des Kontext setzen können.

[self.layer addSublayer: Erde]

dann in der übergeordneten UIView Unterklasse können Sie die earth der Ansicht Hierarchie wie folgt hinzu;

Beachten Sie, dass earth selbst ein CALayer mit einem Bild als Inhalt, damit ich es direkt als Unterschicht hinzuzufügen. Die andere Sache, die ich immer vergesse, ist die Eigenschaft frame des Layers. Sonst ist seine Breite und Höhe Null und wird daher nicht angezeigt.

In Ihrem Code haben Sie einen Layer erstellt, aber Ihren Layer nie zur Ansichtshierarchie hinzugefügt. Es gibt einen Baum für Views (Klasse UIView) und ihre Subviews. Die Wurzel des Baumes ist [UIWindow Hauptfenster] (beachten Sie, dass UIWindow eine Unterklasse von UIView ist. Jeder UIView eine layer Eigenschaft des Typs hat CALayer die ihren sichtbaren Inhalt tatsächlich hält. UIView ist nur ein Wrapper um CALayer, um Dinge wie Touch-Handling Fähigkeit hinzufügen Die wichtige Idee, die Ihnen hier fehlt, ist, dass alles, was auf dem Bildschirm sichtbar sein soll, Teil dieses Baums von Ansichten mit einem UIWindow als Wurzel ist und dass alle Ansichten sichtbar sein müssen Eigenschaft auf NO) zwischen dem Toplevel-Fenster und dem betreffenden Layer. Nur dann wird eine Ebene auf dem Bildschirm sichtbar sein.So irgendwo in Ihrem Code müssen Sie Ihre Schicht als eine Unterschicht von Som hinzufügen alles bereits sichtbar unter addSublayer:.

In Ihrem Fall, ich bin earth unter der Annahme ist eine Instanz eines CALayer und was Sie im Inneren des loadImage: Verfahren getan haben, ist ein CALayer (mit einem Aufruf an [CALayer layer]) innerhalb der earth Schicht neu erstellen, aber dann kannst du nie neue Ebene zu etwas, so dass es nie sichtbar wurde. Was Sie getan haben stattdessen versucht, fügen Sie die earth Schicht, die noch keinen Inhalt haben, zu dem, was ich davon ausgehen, ist eine UIView ‚s Schicht

[[self Schicht] addSublayer: [Erdschicht]];

Ein Problem mit dieser Linie ist, dass Sie scheinen layer zu denken eine Eigenschaft von earth ist. Tatsächlich ist layer eine Klassenmethode von CALayer, die eine Factory-Methode zum Erstellen neuer Instanzen von CALayer ist. Dies ist verwirrend, da [self layer] tatsächlich auf die Layer-Eigenschaft einer UIView Instanz zugreift. (Ich nehme an, self bezieht sich auf eine Instanz einer UIView Unterklasse.)

Verwandte Themen