Как инициализировать свойства, которые зависят друг от друга

Я хочу, чтобы изображение переместилось на дно. Если я нажму кнопку, рис. Должен сдвинуться вниз на 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);
    }
}

Ответ 1

@MartinR указал на главную проблему:

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

Проблема в том, что инициализатор по умолчанию Swift не может ссылаться на значение другого свойства, потому что во время инициализации свойство еще не существует (поскольку сам экземпляр еще не существует). В принципе, в panzer инициализаторе по умолчанию вы неявно ссылаетесь на self.corX и 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 и его свойства существуют.

Ответ 2

Ваше зависимое свойство должно быть:

  • lazy
  • Имейте явный : Type
  • Используйте 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'

Ответ 3

Я обращаюсь к названию вопроса:

Оба ленивых и вычисленных свойства помогут вам справиться, когда начальное значение свойства неизвестно до тех пор, пока объект не будет инициализирован. Но есть некоторые отличия. Я выделил выделение жирным шрифтом.

Если вам просто нужно инициализировать переменную после того, как инициализирована какая-либо другая переменная (ы), вы должны использовать lazy, то есть, если точка состоит в том, чтобы просто добавить задержку (так что все необходимые свойства инициализируются раньше), тогда использование lazy правильный путь для этого.

Но если вам нужно постоянно изменять переменную на основе другой, то вам нужно вычислить свойство, которое будет работать в обоих направлениях:

  • если вычисленное свойство установлено, оно устанавливает переменные связанные с ними хранимые свойства
  • если сохраненные свойства будут установлены (или будут снова reset), тогда это вызовет изменение в вычисленном свойстве.

, если вы измените значение lazy property, это не повлияет на этажные свойства, на которых он был основан. см. здесь


Хорошим примером использования ленивого свойства было бы то, что если у вас есть firstName и lastName, тогда вы лениво создаете экземпляр fullName и, скорее всего, вы никогда не измените имя firstName lastName вашего объекта. Ваше полное имя является одноразовым только... Или, возможно, что-то, что может быть сделано только с помощью свойств lazy, заключается в том, что до тех пор, пока вы не получите доступ к свойству , он никогда не будет инициализирован, поэтому это уменьшит нагрузку инициализации вашего класса. Загрузка вычисления тяжелый.

Кроме того, используя lazy будет сигнал другим разработчикам: "Эй сначала прочитайте о других свойствах и поймите, что они... затем приходят к этому ленивому имуществу... поскольку значение этого основано на них + это, вероятно, тяжелое вычисление, к которому нельзя получить доступ слишком рано..."

Как и для вычисленного свойства, хорошим примером может быть, если вы установите температуру в Фаренгейт, тогда вы также хотите, чтобы ваша температура Цельсия изменила его значение... и если вы установите температуру Цельсия, то снова вы хотите изменить значение Фаренгейта.

В результате вычисленное свойство добавит дополнительные вычисления... и если ваш расчет очень прост и не называется слишком часто, тогда ему не о чем беспокоиться, но если он вызван слишком часто или очень сильно потребляет процессор, тогда он может быть лучше подумать о других вариантах...

Ответ 4

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