78

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

4 回答 4

66

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

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

问题是 Swift 默认初始化器不能引用另一个属性的值,因为在初始化时,该属性还不存在(因为实例本身还不存在)。基本上,在panzer的默认初始化程序中,您隐含地指的是self.corXand self.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它的属性就存在了。

于 2014-09-15T20:55:26.590 回答
10

您的附属财产必须是:

  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'
于 2017-02-09T08:12:54.220 回答
1

我正在解决问题的标题:

惰性属性和计算属性都可以帮助您处理在对象初始化之前知道属性初始值的情况。但也有一些区别。我用粗体突出了差异。

如果您只需要在初始化其他一些变量之后初始化一个变量,那么您应该使用lazyie 如果要点是简单地添加延迟(因此所有必需的属性都在之前初始化)那么使用惰性是正确的方法它。

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

  • 如果设置了计算属性,那么它将变量设置为相关的存储属性
  • 如果存储的属性已设置(或再次重置),那么它将触发计算属性的更改。

如果您更改惰性属性的值,它不会影响它所基于的故事属性。看这里


使用惰性属性的一个很好的例子是,一旦你拥有firstName&lastName然后你就会惰性地实例化 afullName并且很可能你永远不会改变你的对象的 firstName lastName 你的 fullName 是一次性的......或者也许只能做一些事情by lazyproperties 是直到你不访问它永远不会被初始化的属性,因此这将减少你的类的初始化负载。加载繁重的计算。

另外使用lazy意志向其他开发人员发出信号:“嘿,首先阅读其他属性并了解它们是什么......然后来到这个懒惰的属性......因为它的价值是基于它们+这可能是一个沉重的不应该过早访问的计算......”

至于计算属性,一个很好的例子是,如果您将温度设置为华氏温度,那么您还希望您的摄氏温度改变它的值……如果您设置了摄氏温度,那么您又想改变您的华氏温度值。

结果,计算属性会增加额外的计算...如果您的计算非常简单并且调用频率不高,则无需担心,但如果调用频率过高或非常消耗 CPU,那么它可能会更好考虑其他选择...

于 2017-05-03T16:09:40.030 回答
-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()
于 2018-05-19T16:53:05.953 回答