Xcode 如何初始化相互依赖的属性

Xcode 如何初始化相互依赖的属性,xcode,swift,frame,cgrectmake,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,

我想要一张照片移到底部。如果我按下按钮,图片应该向下移动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没有成员名称窗格,我在其中获取错误//以显示在哪行中

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

@马丁纳在这里指出了主要问题:

var corX = 0
var corY = 0
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40))
问题是Swift默认初始值设定项不能引用另一个属性的值,因为在初始化时,该属性还不存在(因为实例本身还不存在)。基本上,在<代码> PANZER <代码>的默认初始化器中,你隐含地引用了代码>自我> CORX 和 >科丽。 一种解决方法是使初始值设定项变为惰性:

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

您的从属属性需要:

  • lazy
  • 有一个明确的
    :键入
  • 使用
    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'
    

    我在回答问题的标题:

    惰性属性和计算属性都可以帮助您处理在对象初始化之后才知道属性初始值的情况。但也有一些不同之处。我用粗体字强调了不同之处

    如果您只是需要在其他变量初始化之后初始化一个变量,那么您应该使用
    lazy
    即如果只是简单地添加一个延迟(这样所有必需的属性都会在之前初始化),那么使用lazy是正确的方法

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

    • 如果已设置计算属性,则会设置变量及其相关存储属性
    • 如果设置(或再次重置)存储的属性,则会触发对计算属性的更改
    如果更改lazy属性的值,则不会影响它所基于的storied属性。看


    使用lazy属性的一个很好的例子是,一旦您拥有了
    firstName
    &
    lastName
    ,那么您将惰性地实例化
    fullName
    ,并且您可能永远不会更改对象的firstName lastName您的fullName是一次性的。。。 或者,可能只有
    lazy
    属性才能完成的一件事是,直到您不访问属性为止,它永远不会被初始化,因此这将减少类的初始化负载。加载一个沉重的计算

    此外,使用
    lazy
    向其他开发人员发出信号:“嘿,首先去阅读其他属性,了解它们是什么……然后来看看这个lazy属性……因为它的值是基于它们的+这可能是一个繁重的计算,不应该过早访问…”

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


    因此,computed属性会增加额外的计算量……如果您的计算非常简单且调用不太频繁,则无需担心,但如果它被调用太频繁或占用大量CPU,则最好考虑其他选项……

    “我使用Xcode Beta5”为什么?Xcode 6已经通用了。总是使用最新版本,尤其是在Swift语言不断变化的情况下。是的,我知道,但我是度假回来的,还没有这么做。但是更新并不能解决问题。听起来你把代码放错地方了,例如,
    var corX=0
    不在
    声明的顶层。发布整个源文件。你能显示周围的上下文吗?(是的,就像@robmayoff所说的那样。)而且,这次请更加努力地正确格式化它。没有人喜欢懒得格式化代码的海报……你试过编译吗?首先,在当前的Xcode版本中,没有
    @
    ,它是
    懒惰的
    ,即使如此,我仍然会收到相同的错误消息。@MartinR很好的调用。都修好了@matt我仍然会收到一个更改错误:
    'ViewController->()->ViewController!'没有名为“corX”的成员。
    @MikeS听起来像是您键入的,而不是复制和粘贴的。你不能漏掉任何东西。例如,如果省略UIImageView类型声明,将出现该错误。或者,如果您省略了惰性声明,您将得到该错误。这一切都是必要的。直接从浏览器复制并粘贴到代码中。它在当前版本的Xcode(Xcode 6 GM)中编译得很好。@matt,你的指令不起作用,接受它吧。只有pkamb说明了消除编译器错误应满足的3个条件:
    惰性
    .self
    :键入
    。虽然您使用了
    :Type
    ,但您没有说明这一点很重要。你的答案要长得多,也更难读。我认为它拥有更多的选票是不公平的。这个答案如何适用于这个问题?
    //
    //  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()