So initialisieren Sie voneinander abhängige Eigenschaften

77

Ich möchte, dass ein Bild nach unten verschoben wird. Wenn ich einen Knopf drücke, sollte sich das Bild um 1 nach unten bewegen.

Ich habe das Bild und einen Button hinzugefügt:

var corX = 0
var corY = 0

var runter: UIButton = UIButton.buttonWithType(UIButtonType.System) as UIButton

var image = UIImage(named: "panzerBlau.jpg");
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40));  //

override func viewDidLoad() {
    super.viewDidLoad()

    panzer.image = image;    //
    self.view.addSubview(panzer);    //

    runter.frame = CGRectMake(100, 30, 10 , 10)
    runter.backgroundColor = UIColor.redColor()
    view.addSubview(runter)
    runter.addTarget(self, action: "fahren", forControlEvents:UIControlEvents.TouchUpInside)
}

Zumindest sagte ich in Funktion "fahren", um das Bild um 1 nach unten zu verschieben.

func fahren(){
    corY += 1
    panzer.frame = CGRectMake(corX, corY, 30, 40) //
    self.view.addSubview(panzer);
}

Mein Problem ist also: Ich bekomme mehrere Fehler mit diesen corX- und corY-Dingen. Ohne sie funktioniert es perfekt, aber es ist wie ein Einwegknopf. Die Fehler sind: ViewController.Type hat kein Mitglied mit dem Namen corX und ViewController.Type hat kein Mitglied mit dem Namen panzer. Woher bekomme ich die Fehler, die ich // gemacht habe, um anzuzeigen, in welchen Zeilen.

PS: Ich benutze Xcode Beta5

Hier ist der vollständige Code ohne irgendetwas anderes:

import UIKit

class ViewController: UIViewController {

    var corX = 0
    var corY = 0
    var runter: UIButton = UIButton.buttonWithType(UIButtonType.System) as UIButton
    var image = UIImage(named: "panzerBlau.jpg");
    var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40));

    override func viewDidLoad() {
        super.viewDidLoad()
            panzer.image = image;
            self.view.addSubview(panzer);

        runter.frame = CGRectMake(100, 30, 10 , 10)
        runter.backgroundColor = UIColor.redColor()
        view.addSubview(runter)
        runter.addTarget(self, action: "fahren", forControlEvents:UIControlEvents.TouchUpInside)
    }

    func fahren(){
        corY += 100
        panzer.frame = CGRectMake(corX, corY, 30, 40)
        self.view.addSubview(panzer);
    }
}
Lukas Köhl
quelle
2
"Ich benutze Xcode Beta5" Warum? Xcode 6 ist GM geworden. Verwenden Sie immer die neueste Version, insb. da sich die Swift-Sprache ständig ändert.
Matt
Ja, ich weiß, aber ich kam aus dem Urlaub und habe das noch nicht getan. Ein Update löst das Problem jedoch nicht.
Lukas Köhl
Es hört sich so an, als hätten Sie Ihren Code an der falschen Stelle abgelegt, z. B. var corX = 0befindet er sich nicht auf der obersten Ebene der classDeklaration.
Matt
2
Veröffentlichen Sie die gesamte Quelldatei.
Rob Mayoff
1
Können Sie den umgebenden Kontext zeigen? (Ja, wie @robmayoff gesagt hat.) Und bitte arbeiten Sie diesmal härter daran, es richtig zu formatieren. Niemand mag ein Poster, das zu faul ist, um seinen Code zu formatieren ...
Matt

Antworten:

65

@MartinR hat hier auf das Hauptproblem hingewiesen:

var corX = 0
var corY = 0
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40))

Das Problem ist, dass ein Swift-Standardinitialisierer nicht auf den Wert einer anderen Eigenschaft verweisen kann, da die Eigenschaft zum Zeitpunkt der Initialisierung noch nicht vorhanden ist (da die Instanz selbst noch nicht vorhanden ist). Grundsätzlich panzerbeziehen Sie sich im Standardinitialisierer implizit auf self.corXund self.corY- aber es gibt keine, selfweil selfgenau das ist, was wir gerade erstellen.

Eine Problemumgehung besteht darin, den Initialisierer faul zu machen:

class ViewController: UIViewController {
    var corX : CGFloat = 0
    var corY : CGFloat = 0
    lazy var panzer : UIImageView = UIImageView(frame: CGRectMake(self.corX, self.corY, 30, 40))
    // ...
}

Das ist legal, da panzeres erst später initialisiert wird, wenn es zum ersten Mal von Ihrem eigentlichen Code referenziert wird. Zu diesem Zeitpunkt selfund seine Eigenschaften existieren.

matt
quelle
6
Haben Sie versucht, das zu kompilieren? Erstens ist es lazyohne @in der aktuellen Version von Xcode, und selbst dann ich immer noch die gleichen Fehlermeldung.
Martin R
@ MartinR Guter Anruf. Alles jetzt behoben!
Matt
@ Matt Ich bekomme immer noch einen Fehler mit dieser Änderung:'ViewController -> () -> ViewController!' does not have a member named 'corX'
Mike S
2
@MikeS Es hört sich so an, als hätten Sie es eingegeben, anstatt es zu kopieren und einzufügen. Sie dürfen nichts auslassen. Wenn Sie beispielsweise die UIImageView-Typdeklaration weglassen, wird dieser Fehler angezeigt. Oder wenn Sie die Lazy-Deklaration weglassen, wird dieser Fehler angezeigt. Es ist alles notwendig. Kopieren Sie es und fügen Sie es direkt aus Ihrem Browser in Ihren Code ein. Es wird in der aktuellen Version von Xcode (Xcode 6 GM) problemlos kompiliert.
Matt
3
@matt, deine Anweisungen funktionieren nicht, akzeptiere sie einfach. Nur pkamb gibt drei Bedingungen an, die erfüllt sein sollten, um den Compilerfehler zu beseitigen : lazy, .selfund : Type. Obwohl Sie verwendet haben, haben : TypeSie nicht gesagt, dass dies wichtig ist. Und Ihre Antwort ist viel länger und schwerer zu lesen. Ich denke, es ist nicht fair, dass es mehr positive Stimmen gibt.
Kelin
10

Ihr abhängiges Eigentum muss sein:

  1. lazy
  2. Habe eine explizite : Type
  3. Verwenden Sie self.diese Option, um auf andere Eigenschaften zuzugreifen

Beispiel:

let original = "foo"

// Good:
lazy var depend: String = self.original

// Error:
     var noLazy: String = self.original // Error: Value of type '(NSObject) -> () -> URLData' has no member 'original'
lazy var noType         = self.original // Error: Value of type '(NSObject) -> () -> URLData' has no member 'original'
lazy var noSelf: String =      original // Error: Instance member 'original' cannot be used on type 'YourClass'
pkamb
quelle
1

Ich spreche den Titel der Frage an:

Beide faul und berechneten Eigenschaften helfen Sie befassen sich mit , wenn der Anfangswert für eine Eigenschaft ist nicht bekannt , bis , nachdem das Objekt initialisiert wird. Es gibt jedoch einige Unterschiede. Ich habe die Unterschiede fett hervorgehoben.

Wenn Sie lediglich eine Variable initialisieren müssen, nachdem eine andere Variable initialisiert wurde, sollten Sie Folgendes verwenden: lazyWenn Sie lediglich eine Verzögerung hinzufügen möchten (damit alle erforderlichen Eigenschaften zuvor initialisiert werden), ist die Verwendung von Lazy der richtige Weg es.

Wenn Sie jedoch eine Variable basierend auf einer anderen ständig ändern müssen, benötigen Sie eine berechnete Eigenschaft, die in beide Richtungen funktioniert :

  • Wenn die berechnete Eigenschaft festgelegt ist, werden die Variablen mit den zugehörigen gespeicherten Eigenschaften festgelegt
  • Wenn die gespeicherten Eigenschaften festgelegt (oder erneut zurückgesetzt ) werden, wird eine Änderung der dann berechneten Eigenschaft ausgelöst.

Wenn Sie den Wert der Lazy-Eigenschaft ändern, wirkt sich dies nicht auf die gespeicherten Eigenschaften aus, auf denen sie basiert. siehe hier


Ein gutes Beispiel für die Verwendung einer Lazy-Eigenschaft wäre, dass Sie, sobald Sie firstName& lastNamedann haben, einen faul instanziieren fullNameund wahrscheinlich niemals den Vornamen Nachnamen Ihres Objekts ändern würden. Ihr vollständiger Name ist nur einmalig ... oder vielleicht etwas, das nur getan werden kann Nach lazyEigenschaften wird sie so lange nicht initialisiert , bis Sie nicht auf die Eigenschaft zugreifen. Daher würde dies die Initialisierungslast Ihrer Klasse verringern . Laden einer schweren Berechnung.

Zusätzlich wird die Verwendung lazywird das Signal an andere Entwickler: „Hey zuerst über die anderen Eigenschaften als gelesen und verstehen , was sie sind ... dann auf diese faul Eigenschaft kommen ... da der Wert dieser auf ihnen basiert + dies ist wahrscheinlich eine schwere Berechnung, auf die nicht zu früh zugegriffen werden sollte ... "

Ein gutes Beispiel für die berechnete Eigenschaft wäre, wenn Sie die Temperatur auf Fahrenheit einstellen, dann möchten Sie auch, dass Ihre Celsius- Temperatur ihren Wert ändert ... und wenn Sie die Celsius- Temperatur einstellen , möchten Sie erneut Ihren Fahrenheit- Wert ändern .

Infolgedessen würde die berechnete Eigenschaft zusätzliche Berechnungen hinzufügen ... und wenn Ihre Berechnung sehr einfach ist und nicht zu häufig aufgerufen wird, besteht kein Grund zur Sorge, aber wenn sie zu oft aufgerufen wird oder sehr CPU-aufwendig ist, ist sie möglicherweise besser an andere Möglichkeiten denken ...

Honig
quelle
-1
//
//  ViewController.swift
//
//  Created by Shivank Agarwal on 19/05/18.
//  Copyright © 2018 Shivank Agarwal. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    var corX = 0
    var corY = 0
    var runter: UIButton = UIButton()
    var image = UIImage(named: "panzerBlau.jpg")
    var panzer = UIImageView()

    override func viewDidLoad() {
        super.viewDidLoad()
        panzer.image = image;
        self.view.addSubview(panzer);
        panzer.frame = CGRect(x: CGFloat(corX), y: CGFloat(corY), width: 30, height: 40)
        runter.backgroundColor = UIColor.red
        view.addSubview(runter)
        view.addSubview(panzer)
        runter.addTarget(self, action: Selector(("fahren")), for:UIControlEvents.touchUpInside)
    }

    private func fahren(){
        corY += 100
    }

    private func updatePanzerFrame(){
        panzer.frame = CGRect(x: CGFloat(corX), y: CGFloat(corY), width: 30, height: 40)
    }
}

Note: Do not add panzer imageView every time when user tap only add it on viewDidLoad()
CrazyPro007
quelle
Wie trifft diese Antwort auf diese Frage zu?
pkamb