Anfängerfehler: NSView in Subklasse von NSView einfügen.

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Anfängerfehler: NSView in Subklasse von NSView einfügen.

    Hallo,

    ich habe offensichtlich ein Verständnisproblem mit xCode und Objective C. Ich erstelle eine Subklasse von NSView, ziehe einen CustomView in den IB (und markiere ihn entsprechend als Subclass von NSView) und möchte diesem NSView einen weiteren NSView hinzufügen. Leider klappt das nicht so, wie ich es mir vorstelle. Anbei der Code:

    Quellcode

    1. #import "myView.h"
    2. @implementation myView
    3. - (void)drawRect:(NSRect)dirtyRect {
    4. [super drawRect:dirtyRect];
    5. [self setFrame: [[self superview]frame]];
    6. [[NSColor blueColor] set];
    7. NSRectFill([self bounds]);
    8. // Drawing code here.
    9. }
    10. -(void)awakeFromNib{
    11. NSView* subView = [[NSView alloc] initWithFrame: NSMakeRect(10, 10, 100, 100)];
    12. [[NSColor redColor] set];
    13. NSRectFill([subView bounds]);
    14. [self addSubview:subView];
    15. }
    Alles anzeigen
    Es wird zwar ein NSView angezeigt, das mit blauer Farbe gefüllt ist, jedoch wird kein rotes Rechteck (wiederum ein NSView) gezeichnet.
    Ich sitze schon länger an dem Problem und finde leider keine Lösung.

    Viele Grüße,
    Ingo44
  • Hallo,

    Zeile 7 ist falsch…
    Du kannst nicht die Box von dem Superview verändern, wenn Du am Zeichnen bist.
    "Können" schon, aber das Verhalten wird immer dubios sein.

    Zeile 15/16 ist falsch…
    Du kannst da nicht zeichnen.

    Und so geht's:

    - MyRedView erstellen
    - -drawRect: mit rot ausmalen

    - MyBlueView erstellen
    - -drawRect: mit blau ausmalen

    Dein -awakeFromNib: erzeugt dann eine "rote Instanz" und fügt die hinzu.

    Viele Grüße
  • Danke dir für die Antwort. Jetzt habe ich versucht, das mal so umzusetzen und es erscheinen auch beide Rechtecke.
    Ich möchte das blaue Rechteck an die Größe des Fensters anpassen sodass ich einen IBOutlet NSWindow erstellt habe und ihn mit dem Window verbunden habe.

    Das blaue Rechteck erscheint nun in der oberen linken Ecke und das rote Rechteck ist auch zu sehen, jedoch noch am falschen Punkt.

    Anbei mein Code:

    Quellcode

    1. - (void)drawRect:(NSRect)dirtyRect {
    2. [super drawRect:dirtyRect];
    3. [[NSColor blueColor] set];
    4. NSRectFill([self bounds]);
    5. [[NSColor redColor]set];
    6. NSRectFill([myRedView bounds]);
    7. // Drawing code here.
    8. }
    9. -(void)awakeFromNib{
    10. [self setFrame:[myWindow frame]];
    11. myRedView = [[NSView alloc] initWithFrame:NSMakeRect(50, 50, 50, 50)];
    12. [self addSubview: myRedView];
    13. }
    Alles anzeigen
    Vielleicht kann mir jemand kurz einen vollständigen Code für mein Vorhaben schreiben ? Das würde ich dann zu Lernzwecken einsetzen.

    Gruß.
    Ingo
  • Da hängt im moment garnix am fenster.

    auch ist das myRedView komplett für die katz weil es nirgendwo verwendet wird. du verwendest nur die bounds davon, also kannst du ja gleich die verwenden.

    wenn du das view mit dem fenster vergrößern willst dann ist zb autolayout di lösung (oder das alte verfahren dessen namen mir nicht mehr einfällt - nimm aber besser autolayout).
  • Vielen Dank. Was mein Problem ist: Ich verstehe das Prinzip nicht, wie wie man die Elemente, welche uns Apple zu Verfügung stellt, ändert.
    Hierbei meine ich die Objekte im Interface Builder (NSView, NSScrollView, NSTextField...).

    Erstellt man immer eine Subclass und ändert die Eigenschaften bereits dort, oder wird eine Art "Controller"-Objekt erstellt und dort wird alles gesteuert ? (In etwas: Erzeuge Instanz der Subklasse XY und füge ihr etwas hinzu.). Des Weiteren verstehe ich nicht, wann man etwas in eine Subklasse programmiert und wann außerhalb dieser. Beispielsweise habe ich nun gelernt, dass man "drawRect: (NSRect) dirtyRect" nur in der Subklasse verändert. Aber warum kann ich die Subklasse nicht "leer" lassen und im Controller einer Instanz der Subklasse die Nachricht schicken, dass sie sich malen soll.

    Es geht mir vorerst um das Prinzip. Es ist mit leider noch unverständlich.

    Viele Grüße,
    Ingo
  • Ich glaube du hast das konzept nicht ganz verstanden.

    Du hast ein view das irgendwas malen soll (oder auch nur subviews halten etc).
    Wenn du zb gerne eine view hättest die einfach vollflächig die farbe ROT malt, dann mach dir eine subclass von NSView mit zb dem namen MyRedBackgroundView. Diese malt dann in der draw rect eben die rote fläche. Da diese view ja recht dumm ist und sonst nichts kann, braucht man sie auch nicht konfigurieren.
    Wenn du aber zb gerne ein view hättest das jede farbe annehmen kann, dann mach dir eine subclass von NSView mit zb dem namen MyBackgroundView. Häng eine NSColor property namens backgroundColor dran. In der draw rect setzt du dann die backgroundColor und malst mit dieser.
    Diese klasse kannst du konfigurieren (die farbe setzen). Das kannst du von einem controller aus oder eben auch über den interface builder oder auch über bindings.
  • Genau das denke ich auch. Das zugrundeliegende Konzept habe ich noch nicht wirklich verinnerlicht. Wenn ich ein etwas komplexere Klasse per IB erstelle, sagen wir einen NSTextView, dann wird gleichzeitig ein NSScrollView, ein NSClipView und ein NSTextView erstellt. Jetzt möchte ich dem NSTextView sagen, er soll mit der Breite X und der Höhe Y sich mittig im NSScrollView positionieren. AutoLayout kann ich hier nicht direkt verwenden. Die Buttons sind transparent.

    Macht es jetzt Sinn eine Subklasse von NSClipView zu erstellen und ihr Grenzen zuzuweisen oder eine Controllerobjekt zu erstellen, dass Instanzen von NSScrollView und NSClippView erstellt und an diese Nachrichten schickt. Das ist eben der Punkt, wo ich sehr oft Probleme habe und mir nicht weiterhelfen kann. Stehe hier sehr oft auf dem Schlauch.

    Gruß,
    Ingo
  • Nein, das scrollview und clip-view sollst du nicht anfassen.
    was meinst du mit breite X und höhe Y mittig?
    willst du den text mittig platzieren oder die gesamte textview?
    falls ersters, dann stell das beim textview so ein, falls zweiteres dann wendest du die entsprechenden autolayout-constraints auf den scrollview an. also dass dieser mittig in deinem fenster liegt (dieser enthällt dann ja den textview).
  • Das NSTextView sollte mittig auf dem Bildschirm platziert sein und eine vorher definierte Breite haben (weißer Bereich wo man tippen kann). Das NSScrollView sollte jedoch den ganzen Bildschirm erfassen. Der Bereich links und rechts zwischen den Grenzen des NSTextView und NSScrollView sollte grau hinterlegt sein. Es sollte also in etwa wie bei Word aussehen, d.h. weißes bearbeitfähiges Papier in der Mitte uns links und rechts graue Flächen.
  • Achso, du fängst dann aber doch recht anspruchsvoll an!

    Wenn du das machen willst, bau dir ein eigenes graues view welches du als content-view vom scrollview setzt.
    in dein graues view kannst du dann das textview reinpacken. bzw ein textfield denn das textview ohne scrollview zu benutzen ist etwas tricky und führt sicher zu problemen...
  • Habe das mit dem TextField versucht und es hat wunderbar funktioniert. Aus Interesse habe ich es auch mit dem TextView ausprobiert und es klappt auch. Gerne möchte ich einen NSTextView verwenden, weil ich anschließen Ruler einblenden lassen möchte. Jetzt muss ich dem TextView nur mitteilen, dass er sich mittig (vertikal) in dem NSView positionieren soll, die Höhe des Views annehmen soll, und bei Veränderung des Fensters bis zu einer bestimmten Größe "mitwachsen" soll.

    Hier ist der Programmcode bis jetzt:

    Quellcode

    1. #import "myView.h"
    2. @implementation myView
    3. - (void)drawRect:(NSRect)dirtyRect {
    4. [super drawRect:dirtyRect];
    5. CALayer* myLayer = [CALayer layer];
    6. [myLayer setBackgroundColor:[NSColor grayColor].CGColor];
    7. [self setWantsLayer:YES];
    8. [self setLayer:myLayer];
    9. // Drawing code here.
    10. }
    11. -(void)awakeFromNib{
    12. NSTextView* myTextView = [[NSTextView alloc] initWithFrame:NSMakeRect(10, 10, 400, 400)];
    13. [self addSubview:myTextView];
    14. }
    15. @end
    Alles anzeigen
  • Nur aus Neugierde: Warum darf man das nicht machen ? Es hat funktioniert.

    Ich habe mich daran gehalten und folgendes erstellt. Jetzt kann man den NSTextView erstellen, ist bearbeitbar und erscheint.

    myView: für das große View, das NSTextView enthält.

    Quellcode

    1. @implementation myView
    2. - (void)drawRect:(NSRect)dirtyRect {
    3. [super drawRect:dirtyRect];
    4. // Drawing code here.
    5. }
    6. -(void)awakeFromNib{
    7. CALayer* myLayer = [CALayer layer];
    8. [myLayer setBackgroundColor:[NSColor grayColor].CGColor];
    9. [self setWantsLayer:YES];
    10. [self setLayer:myLayer];
    11. }
    Alles anzeigen

    myViewForNSTextView: es enthält das NSTextView

    Quellcode

    1. - (void)drawRect:(NSRect)dirtyRect {
    2. [super drawRect:dirtyRect];
    3. // Drawing code here.
    4. }
    5. -(void)awakeFromNib{
    6. NSTextView* myTextView = [[NSTextView alloc]initWithFrame:NSMakeRect(0, 0, 200, 100)];
    7. [self addSubview:myTextView];
    8. }
    Das Setzen der Constraints im IB bereitet mir noch Probleme. Der View sollte horizontal mittig mit einem gewissen Abstand von unten eingeblendet sein. Er sollte auch eine definierte Breite vom annehmen, die sich mit Veränderung des Fenster verändert, jedoch einen Maximalwert nicht überschreitet. Verkleinert sich das Fenster, dann sollte das View ebenfalls kleiner werden, jedoch eine bestimmte Untergrenze nicht unterschreiten. Ab diesem Punkt sollte das Fenster, NSView "abdecken".

    Kann man eine solche Begrenzung im IB erstellen ?

    Danke für die gute Hilfe !

    Gruß,
    Ingo
  • Alles klar, das werde ich nicht mehr machen.
    Ich denke ich habe es mit den Constraints geschafft, es fehlt nur eine Kleinigkeit:

    Wie kann ich dem NSTextView mitteilen, dass es die gleiche Größe dem NSViews annehmen soll, wenn das Fenster sich vergrößert ?

    ich habe es versucht mit:

    Quellcode

    1. -(void)awakeFromNib{
    2. NSTextView* myTextView = [[NSTextView alloc]init];
    3. [myTextView setFrame:[self frame]];
    4. [self addSubview:myTextView];
    5. }
  • Das habe ich gemacht :)

    Den Frame habe ich durch [myTextView setFrame:[self bounds]]; festgelegt.

    Jetzt erscheint das View, in der Größe in der ich es haben möchte. Es ist immer mittig und wird erst dann in sich verkleinert, wenn der Fensterrand das View erreicht hat. So ist es schonmal super.

    Leider wird nur ca. 60 % des Views durch das NSTextView bedeckt. Woran kann das liegen ?

    Ich habe aus Interesse das ganze View blau zeichnen lassen und es erscheint auch in der Größe blau, die erwünscht ist. (hier: [self bounds]). Wenn ich aber den Frame als "[self bounds]" setze, dann erscheint das NSTextView nicht im vollen View.

    Das sind zum Beispiel genau die Aspekte in Objective C die ich nicht verstehe und gerne lernen würde. Woher weiß man so etwas, dass Views unterschiedliche auf den gleichen Code reagieren ? Erfahrung ? Bücher ? ;)

    Danke für die sehr guten Erklärungen.

    Gruß,
    Ingo
  • Ingo44 schrieb:

    Das habe ich gemacht :)

    Den Frame habe ich durch [myTextView setFrame:[self bounds]]; festgelegt.

    Jetzt erscheint das View, in der Größe in der ich es haben möchte. Es ist immer mittig und wird erst dann in sich verkleinert, wenn der Fensterrand das View erreicht hat. So ist es schonmal super.

    Leider wird nur ca. 60 % des Views durch das NSTextView bedeckt. Woran kann das liegen ?

    Ich habe aus Interesse das ganze View blau zeichnen lassen und es erscheint auch in der Größe blau, die erwünscht ist. (hier: [self bounds]). Wenn ich aber den Frame als "[self bounds]" setze, dann erscheint das NSTextView nicht im vollen View.

    Das sind zum Beispiel genau die Aspekte in Objective C die ich nicht verstehe und gerne lernen würde. Woher weiß man so etwas, dass Views unterschiedliche auf den gleichen Code reagieren ? Erfahrung ? Bücher ? ;)

    Danke für die sehr guten Erklärungen.

    Gruß,
    Ingo
    frame und bounds sind nun mal zwei andere eigenschaften sonst gäbe es nicht zwei methoden dafür.