How to initialize properties that depend on each other
我希望图片移到底部。 如果按按钮,图片应向下移动1。
我添加了图片和一个按钮:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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。
1 2 3 4 5 | 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
这是没有其他内容的完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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); } } |
@MartinR在这里指出了主要问题:
1 2 3 | var corX = 0 var corY = 0 var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40)) |
问题在于,Swift默认初始化程序无法引用另一个属性的值,因为在初始化时,该属性尚不存在(因为实例本身尚不存在)。基本上,在
一种解决方法是使初始化程序变得懒惰:
1 2 3 4 5 6 | class ViewController: UIViewController { var corX : CGFloat = 0 var corY : CGFloat = 0 lazy var panzer : UIImageView = UIImageView(frame: CGRectMake(self.corX, self.corY, 30, 40)) // ... } |
这是合法的,因为
您的依附财产必须是:
例:
1 2 3 4 5 6 7 8 9 | 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' |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | // // 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() |
我正在解决问题的标题:
直到对象初始化之后才知道属性的初始值时,惰性属性和计算属性都可以帮助您处理。但是有一些区别。我用粗体突出显示了差异。
如果您只需要在初始化一些其他变量后初始化一个变量,则应该使用
但是,如果您需要不断地基于另一个变量来更改变量,则需要一个可以同时起作用的计算属性:
- 如果计算出的属性集,则将变量设置为其相关的存储属性
- 如果已设置(或再次重置)存储的属性,则它将触发随后计算的属性的更改。
如果更改惰性属性的值,则不会影响其基于的存储属性。看到这里 sub>
使用惰性属性的一个很好的例子是,一旦拥有
或者也许只能由
另外,使用
至于计算属性,一个很好的例子是,如果您将温度设置为华氏温度,那么您还希望您的摄氏温度改变它的值...并且如果您设置摄氏温度,那么您又想改变您的华氏温度值。
结果,计算属性将增加额外的计算...如果您的计算非常简单且调用不频繁,则无需担心,但是如果调用过于频繁或占用大量CPU,则可能会更好考虑其他选择...