如何初始化彼此依赖的属性

时间:2014-09-15 18:16:30

标签: xcode swift frame cgrectmake

我想要一张图片移到底部。如果按下按钮,图片应向下移动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)
}

至少我说过功能" fahren"将图片向下移动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);
    }
}

4 个答案:

答案 0 :(得分:59)

@MartinR在这里指出了主要问题:

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

问题是Swift默认初始值设定项不能引用另一个属性的值,因为在初始化时,属性还不存在(因为实例本身尚不存在)。基本上,在panzer的默认初始值设定项中,您隐式引用self.corXself.corY - 但没有self,因为self正是我们正在创造。

一种解决方法是使初始化程序变得懒惰:

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及其属性存在。

答案 1 :(得分:8)

您的受抚养财产必须是:

  1. lazy
  2. 明确: Type
  3. 使用self.访问其他媒体资源
  4. 示例:

    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'
    

答案 2 :(得分:0)

我正在解决问题的标题:

延迟和计算属性都可以帮助您在初始化对象之前处理属性的初始值 not 时的处理。但是有一些不同之处。我用粗体突出了这些差异。

如果你只需要在初始化一些其他变量之后初始化一个变量,那么你应该使用lazy,即如果要点只是添加一个延迟(所以所有必需的属性在之前被初始化)然后使用懒惰是正确的方法。

但是如果您需要不断更改基于另一个的变量,那么您需要一个可以双向的计算属性:

  • 如果计算属性设置,则它将变量设置为相关的存储属性
  • 如果设置了存储的属性(或重新重新),则会触发计算属性的更改。

如果更改惰性属性的值,它将不会影响它所基于的层对象属性。见here

使用懒惰属性的一个好例子是,一旦你有firstName& lastName然后你会懒惰地实例化一个fullName并且你可能永远不会改变你的对象的firstName lastName你的fullName只有一次... 或者只有lazy属性才能完成的事情是,直到你不访问属性它将永远不会被初始化,因此这将减少您的类的初始化负载。加载计算。

另外使用lazy信号发送给其他开发者:“嘿,首先去了解其他属性并了解它们是什么......然后来到这个懒惰的属性......因为这个值基于它们+这可能是一个不应该过早访问的繁重计算......“

至于计算属性,一个很好的例子就是如果你将温度设置为华氏度那么你也希望你的摄氏温度温度改变它的值......如果你设置摄氏度温度然后再次想要更改华氏度值。

结果计算属性会增加额外的计算......如果你的计算非常简单并且没有太频繁地调用那么就没什么可担心的,但是如果它经常被调用或者非常消耗CPU,那么它考虑其他选择可能会更好......

答案 3 :(得分:0)

//
//  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