web-dev-qa-db-ja.com

互いに依存するプロパティを初期化する方法

写真を下に移動したいです。ボタンを押すと、写真が1つ下に移動します。

写真とボタンを追加しました。

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)
}

少なくとも、機能「華氏」で、写真を1つ下に移動すると言いました。

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

だから私の問題は:これらのcorXとcorYのことでいくつかのエラーが出ます。それらがなくても完全に機能しますが、使い捨てのボタンのようなものではありません。エラーは次のとおりです。ViewController.TypeにはcorXというメンバーがなく、ViewController.Typeにはメンバー名panzerがありません。

PS:Xcode Beta5を使用しています

以下に、他に何もない完全なコードを示します。

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);
    }
}
72
Lukas Köhl

@MartinRはここで主要な問題を指摘しています。

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

問題は、Swiftデフォルト初期化子が別のプロパティの値を参照できないことです。初期化の時点で、プロパティがまだ存在しないためです(インスタンス自体がまだ存在しないため) )。基本的に、panzerのデフォルトのイニシャライザーでは、暗黙的にself.corXおよびself.corYを参照しますが、selfは正確には何もないため、selfはありません。作成の途中。

1つの回避策は、初期化子を遅延させることです。

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

panzerは、後で実際のコードで最初に参照されるまで初期化されないため、これは合法です。その頃には、selfとそのプロパティが存在します。

62
matt

依存プロパティは次のとおりである必要があります。

  1. lazy
  2. 明示的な: Type
  3. 使用する self.他のプロパティにアクセスするには

例:

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'
9
pkamb
//
//  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()

Xcode sample

0
CrazyPro007

私は質問のタイトルに取り組んでいます:

遅延プロパティと計算プロパティは、オブジェクトの初期化後までプロパティの初期値がnotである場合に対処するのに役立ちます。しかし、いくつかの違いがあります。違いを太字で強調しました。

他の変数が初期化された後に変数を初期化する必要がある場合は、lazyを使用する必要があります。つまり、単に遅延を追加する場合(したがって、必要なすべてのプロパティが初期化される)それのための正しい方法。

しかし、一定して変数を別の変数に基づいて変更する必要がある場合、両方とも機能する計算プロパティが必要です方法

  • 計算されたプロパティsetの場合、関連する保存されたプロパティを変数に設定します
  • 格納されたプロパティが設定されている(またはリセットされているagain)場合、計算されたプロパティの変更がトリガーされます。

遅延プロパティの値を変更しても、それが基になっている名高いプロパティには影響しません。 こちら をご覧ください


遅延プロパティを使用する良い例は、firstNamelastNameを取得したら、fullNameを遅延的にインスタンス化し、firstNameとlastNameを変更しないことです。オブジェクトのfullNameは1回限りです...または、おそらくlazyプロパティでしかできないことは、プロパティにアクセスしない限り初期化されるまで、したがって、これはクラスの初期化ロードを減少します。 heavy計算の読み込み。

さらにlazyを使用すると、他の開発者にとってsignalになります。「まず、他のプロパティについて読んで、その内容を理解してください...その後、この遅延プロパティに来てください...これの値はそれらに基づいているため+これはおそらくあまりにも早くアクセスされるべきではない重い計算です...」

計算されたプロパティに関しては、温度をFahrenheitに設定すると、摂氏の温度も変更したい場合の良い例です値...そして、摂氏温度を設定した場合、再び華氏値を変更します。

結果として、計算プロパティは追加の計算を追加します...そしてあなたの計算が非常に単純であまり頻繁に呼び出されない場合は心配することはありませんが、あまりにも頻繁に呼び出されるか非常にCPUを消費する場合はより良いかもしれません他のオプションを考える...

0
Honey