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


Я хочу, чтобы изображение переместилось на дно. Если я нажму кнопку, пик должен двигаться вниз на 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 thing. Без них он работает отлично, но чем его как одноразовая кнопка. Ошибки: ViewController.Тип не имеет члена с именем corX и ViewController.Тип не имеет имени члена 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 60

4 ответа:

@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 не инициализируется до тех пор, пока он не будет впервые упомянут вашим фактическим кодом. К тому времени, и его свойства.

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

  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'

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

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

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

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

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

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


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

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

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

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

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