IBOutlets von anderer Swiftdatei ansprechen

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

  • IBOutlets von anderer Swiftdatei ansprechen

    Hallo Zusammen, bin neu hier und auch ein echter Newbie was Swift anbelangt, daher hoffe ich auf Verständnis. Auch bin ich mir unsicher ob der Titel richtig gewählt wurde, aber irgendwie komme ich mit meinem Projekt nicht weiter. Ich habe einen ViewController.swift und eine zusätzliche Datei Game.swift. Dazu habe ich ein paar Labels welche als Outlet im ViewController deklariert sind. Um mehr Übersichtlichkeit zu erreichen, dachte ich mir einige Funktionen in separaten Klassen und diese wiederum in eigene Files zu packen. Grundsätzlich werden keine Fehler ausgegeben, bis zum Zeitpunkt wo ich meine App bzw. das Spiel starten möchte. Anbei ein Foto. Warum kann ich die Labels nicht ansprechen? Freue mich auf eure Antwort!
    Dateien
    </Danke_für_Alles>
  • Game erbt von ViewController. hast du auch Game im Interfacebuilder als den ViewController deklariert der auch angezeigt werden soll? (Im IB -> ViewController auswählen, oben auf Identiy Inspector und dann bei Custom Class Game hinschreiben) Dann brauchst du ihn nicht mehr aufzurufen in viewdidload
  • Das war ja der Sinn der Sache. Ich habe den ViewController im Interfacebilder deklariert (Hier sollen alle Funktionen und Buttons aufgerufen werden). Die eigentlichen Klassen und Funktionen sollen aber in der Game.swift geschrieben werden. Es kommen ja später noch weitere Funktionen hinzu. Dies ist quasi nur der Anfang.
    Wenn doch Game.swift von ViewController.swift erbt, warum lassen sich anscheinen die Outlets nicht verwenden?
    </Danke_für_Alles>
  • Auch auf die Gefahr, mich nun hoffnungslos zu blamieren: So wie ich Dich (und Swift, das ich nicht kenne) verstehe, hast Du zwei Instanzen: eine direkt aus der ViewController-Klasse erzeugt, die andere aus der Tochterklasse Game.

    Wie sollte nun die eine Instanz Zugriff auf Variablen der zweiten haben? Die Klassenhierarchie spielt hierbei m. E. gar keine Rolle, da es nicht um Vererbung von Methoden / Properties, sondern um konkrete Instanzen bzw. deren Property-Inhalte geht.

    Ich würde also der Game-Instanz irgendwie einen Pointer auf die ViewController-Instanz mitgeben, quasi eine Delegation nutzen. Anbieten würde sich diese Zuweisung bei oder direkt nach der Instanzierung von Game, also die Game-Instanz in einer Variablen speichern, dann

    game.delegate = self;

    Anschließend würdest Du die Outlets über diesen Pointer adressieren können, also z. B.

    delegate.numbarLabel.text = "Test";

    So oder so ähnlich, Mattes

    Edit: Alternativ natürlich "Game" direkt als View-Controller nutzen und im IB verdrahten ... dann brauchst Du aber keine extra Instanzierung (und eigentlich auch keine Vererbung). Wie oben geschrieben.
    Diese Seite bleibt aus technischen Gründen unbedruckt.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von MyMattes ()

  • Hi ich check das irgendwie nicht... Kannst du das vielleicht noch ein bisschen leichter erklären :D Es muss doch irgendwie möglich sein, Labels und Textfelder außerhalb des ViewControllers anzusprechen bzw. zu benutzen! Xcode gibt erst keine Fehlermeldung aus, erst beim ausführen. :-/ Ich hau hier noch mal den Code der beiden Dateien rein, vielleicht kann man es besser verstehen. Hoffe ihr schafft es. Liebe Grüße

    C-Quellcode: ViewController.swift = Hier ist der View eingebunden!

    1. import UIKit
    2. class ViewController: UIViewController {
    3. @IBOutlet weak var gameButton: UIButton!
    4. @IBOutlet weak var scoreLabel: UILabel!
    5. @IBOutlet weak var levelLabel: UILabel!
    6. @IBOutlet weak var numbarLabel: UILabel!
    7. @IBOutlet weak var timerLabel: UILabel!
    8. override func viewDidLoad() {
    9. super.viewDidLoad()
    10. Game().newGame()
    11. }
    12. override func didReceiveMemoryWarning() {
    13. super.didReceiveMemoryWarning()
    14. }
    15. }
    Alles anzeigen

    Quellcode: Game.swift

    1. import Foundation
    2. import UIKit
    3. class Game: ViewController {
    4. var score = 0
    5. var level = 1
    6. var timerCount = 10
    7. var randomIndex = 0
    8. var timer = NSTimer()
    9. var numbar = (Int(arc4random_uniform(20)))
    10. var randomNumber = [Int](count: 36, repeatedValue: 1)
    11. func newGame() {
    12. //Initialise
    13. score = 0
    14. level = 1
    15. timerCount = 10
    16. numbarLabel.text = String(numbar)
    17. levelLabel.text = String(level)
    18. timerLabel.text = String(timerCount)
    19. scoreLabel.text = String(score)
    20. //Generate random number for buttons
    21. do {
    22. var randomNumberTemp = (Int(arc4random_uniform(9)+1))
    23. randomNumber[randomIndex] = randomNumberTemp
    24. var randomNumberString = String(randomNumber[randomIndex])
    25. randomIndex++
    26. gameButton.setTitle(randomNumberString, forState: UIControlState.Normal)
    27. } while randomIndex < 35
    28. //Set timer
    29. if timerCount > 0 {
    30. timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("timerFunc"), userInfo: nil, repeats: true)
    31. }
    32. }
    33. func timerFunc() {
    34. if timerCount > 0 {
    35. timerCount -= 1
    36. timerLabel.text = String(timerCount)
    37. } else {
    38. timer.invalidate()
    39. performSegueWithIdentifier("ViewIdentifierGameEnd", sender: nil)
    40. }
    41. }
    42. func buttonPushed() {
    43. var buttonColorSelected = UIColor(red: 0.4, green: 1.0, blue: 0.2, alpha: 1)
    44. var buttonColorDefault = UIColor(red: 1, green: 1, blue: 1, alpha: 1)
    45. var numbarResult = 0
    46. var pushed = false
    47. }
    48. }
    Alles anzeigen
    </Danke_für_Alles>
  • Eigentlich ganz einfach: der im IB angegebene Controller wird automatisch instanziert (A) und hat (direkten) Zugriff auf die Outlets. Wenn eine andere Instanz (B) - auch einer anderen Klasse - die Outlets nutzen soll, muss B eben A kennen. Dies kannst Du erreichen, indem Du nach manuellem Instanzieren von B einen Zeiger auf A an B übergibst, z. B. als Delegate.

    Dieses Konstrukt macht Sinn, wenn Du Funktionen von B nicht direkt im ViewController haben möchtest, weil sie z. B. diesen zu sehr aufblähen oder in mehreren Controllern benutzt werden sollen.

    Ansonsten implementierst Du alle Funktionen von A und B in einem Controller und gibst diesen im IB an ... händisch instanzieren brauchst Du ihn dann nicht mehr.

    HTH, Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Aber habe ich das nicht schon mit dem übergeben der Superklasse gemacht? Denn wenn ich die lösche, markiert er mir die Labels als Falsch an, weil er die nicht kennt s. A. Muss gleich mal gucken wie das mit Swift geht (Pointer), da ich irgendwie nichts mit delegate finden kann. Aber danke, das du mir hilfst :)
    Dateien
    </Danke_für_Alles>
  • Emanicx schrieb:

    Aber habe ich das nicht schon mit dem übergeben der Superklasse gemacht?
    Mit Vererbung übernimmst Du Methoden- und Eigenschaftsdefinitionen, nicht deren Daten. Ich glaube, Du solltest Dich etwas mit den Prinzipien der OOP vertraut machen, dann löst sich Dein Problem fast von selber ... es hat nichts mit Swift zu tun.

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Ja das muss ich auch ehrlich zugeben, taste mich Stück für Stück in die Sache ran. Nur ist das Wissen aus Büchern dann in der Praxis doch noch ein wenig anders :D Trotzdem danke für die Geduld, wenn ich noch was habe melde ich mich, und das kommt bestimmt. ;)
    </Danke_für_Alles>
  • MyMattes schrieb:

    Eigentlich ganz einfach: der im IB angegebene Controller wird automatisch instanziert (A) und hat (direkten) Zugriff auf die Outlets. Wenn eine andere Instanz (B) - auch einer anderen Klasse - die Outlets nutzen soll, muss B eben A kennen. Dies kannst Du erreichen, indem Du nach manuellem Instanzieren von B einen Zeiger auf A an B übergibst, z. B. als Delegate.
    @Emanicx:

    Du bist schuld :D

    Ich habe nun tatsächlich zum ersten Mal ein Swift-Projekt angelegt und 1-2 Zeilen Code geschrieben ... eigentlich nur, um mir mal anzuschauen, wie ich Deine Fragestellung mit Swift lösen würde. Herausgekommen ist folgendes, vielleicht hilft es Dir ja weiter.

    Zunächst der ViewController, der auch im Storyboard referenziert (und deshalb automatisch instanziert) wird:

    Quellcode: ViewController.swift

    1. import UIKit
    2. import GameController
    3. class ViewController: UIViewController
    4. {
    5. @IBOutlet weak var textField: UITextField!
    6. var gameController: GameController!
    7. override func viewDidLoad()
    8. {
    9. super.viewDidLoad()
    10. gameController = GameController()
    11. gameController.delegate = self
    12. }
    13. @IBAction func touchButton(sender: AnyObject)
    14. {
    15. gameController.updateText()
    16. }
    17. }
    Alles anzeigen

    Dieser erzeugt nach dem Lader der View eine Instanz des GameControllers und übergibt diesem einen Zeiger auf sich selbst in das Property "delegate". Wenn ein Button der View berührt wird, ruft die entsprechende Methode des ViewControllers nur eine andere des GameControllers auf, der dann ein Text-Feld aktualisieren soll.

    Der Game-Controller sieht wie folgt aus:

    Quellcode: GameController.swift

    1. import UIKit
    2. class GameController: NSObject
    3. {
    4. weak var delegate: ViewController?
    5. func updateText()
    6. {
    7. delegate?.textField.text = "Outch"
    8. }
    9. }
    Alles anzeigen
    Wie Du siehst, braucht der GameController nicht vom ViewController abstammen. Er nutzt sein Property delegate, das ja seit kurz nach seiner Instanzierung auf den ViewController zeigt, und referenziert über diesen das Text-Feld zur Ausgabe.

    Auch wenn dies Deine Frage beantworten sollte: Guter Programmierstil ist es m. E. nicht. Ich würde alle Elemente einer View immer nur durch einen Objekt (eben den View-Controller) verwalten. Backend-Aktionen in eine andere Klasse auszulagern macht häufig Sinn, aber wenn mehrere Objekte an der gleichen View manipulieren, wird der Code schlecht wart- und wiederverwendbar. Stichwort MVC. Ich würde den GameController Kalkulationen oder was auch immer durchführen lassen und dann wieder eine Methode des ViewControllers aufrufen, um die GUI zu aktualisieren.

    Viel Erfolg, Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von MyMattes () aus folgendem Grund: weak-Referenz auf Delegate zur Vermeidung von retain-cycles.

  • Michael schrieb:

    Übrigens hast du hier einen schönen retain-cycle gebaut. ;)
    Weil der GameController eine starke Referenz auf den ViewController hält, der deshalb nicht automatisch freigegeben werden kann, der GameController wiederum aber auch nicht freigegeben wird? Ich weiß schon, warum mir MRC lieber ist: Da weiß ich (halbwegs), was ich tue ;)

    Naja ist ja nur eine Single-View-App gewesen, da lebt der eh so lange wie die App läuft.

    Cheers, Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Michael schrieb:

    Ob ARC oder MRC spielt ja bei retain-cycles keine Rolle.
    Fachlich schon klar, Michael, aber bei MRC fallen zumindest mir solche Fehler eher auf ... wobei das heute ja eh nur ein quick&dirty-Beispiel für die Fragestellung sein sollte. Keine Entschuldigund, aber eine Erklärung...

    Mattes

    Edit: Also nochmal ganz deutlich für Emanicx, damit er nicht ein fehlerhaftes Beispiel übernimmt: das Property delegate in GameController muss "weak" definiert werden, damit ViewController jemals freigegeben wird. Ändere ich auch noch im Code-Beispiel, ohne es ausprobiert zu haben (iPad).
    Diese Seite bleibt aus technischen Gründen unbedruckt.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von MyMattes ()

  • Puh ziemlich viel Input, trotz all dem klappt das nicht so wirklich. Warum weiß ich nicht. Du hast meine Game.swift ja GameController genannt, muss also im import Game stehen? Ist das überhaupt nötig?
    ABER dachte wirklich das es von der Idee her am Besten wäre, einzelne Funktionen in eigene Files abzuspeichern. Okay werde zukünftig darauf achten. Wenn ich das richtig verstehe, alle UI-Elemente sollten in den MainViewController und alle anderen Funktionen können in separate Files ausgelagert werden.

    Wenn ich noch eine kleine Frage stellen darf: Welche Funktionen sollten in den ViewDidLoad und welche sollten ausserhalb der Funktion? War mir nie wirklich sicher, wo ich meine Funktionen und Befehle packen sollte. Viel lieben Dank für die ganze Mühe, aber ihr habt mir trotzdem viel beigebracht. Danke!
    </Danke_für_Alles>
  • Emanicx schrieb:


    Welche Funktionen sollten in den ViewDidLoad und welche sollten ausserhalb der Funktion?
    Das kannst nur Du entscheiden. Schau Dir mal den Lebenszyklus einer View an, bestimmte Aktionen müssen eben beim Laden, Erscheinen etc. erfolgen. Welche das sind, hängt von Deiner Applikation ab. Ein Startpunkt dürfte die View-Controller-Doku von Apple sein.

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.