Як ініціалізувати властивості, які залежать одна від одної


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

Принаймні, я сказав у функції "Фарень", щоб перемістити зображення вниз на 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);
    }
}

2
"Я використовую Xcode Beta5" Чому? Xcode 6 вийшов GM. Завжди використовуйте останню версію, особливо. оскільки мова Свіфт постійно змінюється.
матовий

Я знаю, але я приїхав із відпустки і ще цього не робив. Але оновлення не вирішить проблему.
Lukas Köhl

Здається, ви помістили свій код не в тому місці, наприклад var corX = 0, не на верхньому рівні classдекларації.
matt

2
Опублікуйте весь вихідний файл.
грабувати майофф

1
Чи можете ви показати навколишній контекст? (Так, як те, що сказав @robmayoff.) І, будь ласка, докладіть більше зусиль, щоб правильно цього форматувати. Нікому не подобається плакат, котрий лінується форматувати свій код ...
matt

Відповіді:


66

@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і його властивості існують.


6
Ви пробували скласти це? По-перше, у поточній версії Xcode це lazyбез @, і навіть тоді я все одно отримую те саме повідомлення про помилку.
Martin R

1
@MartinR Хороший дзвінок. Все виправлено зараз!
matt

@matt Я все одно отримую повідомлення про помилку із цією зміною:'ViewController -> () -> ViewController!' does not have a member named 'corX'
Mike S

2
@MikeS Здається, ви набрали його, а не копіювали та вставляли. Ви не повинні нічого залишати поза увагою. Наприклад, якщо ви пропустите декларацію типу UIImageView, ви отримаєте цю помилку. Або якщо ви пропустите ледаче оголошення, ви отримаєте цю помилку. Це все необхідно. Скопіюйте та вставте з вашого браузера у свій код. Він чудово компілюється в поточній версії Xcode (Xcode 6 GM).
matt

3
@matt, твої вказівки не працюють, просто прийми їх. Тільки pkamb говорить про 3 -х умов , які повинні бути виконані , щоб вирішити проблему компілятора: lazy, .selfі : Type. Хоча ти використовував, : Typeти не казав, що це важливо. І ти відповідаєш набагато довше і важче читаєш. Я думаю, що це нечесно, що він має більше голосів.
келін

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'

1

Я звертаюся до назви запитання:

Обидва ледачих і обчислювані властивості допомагають вам впоратися з тим, коли початкове значення для властивості не НЕ відомо , поки після ініціалізації об'єкта. Але є деякі відмінності. Я виділив відмінності жирним шрифтом.

Якщо вам просто потрібно ініціалізувати змінну після ініціалізації якоїсь іншої змінної, тоді вам слід використовувати, lazyтобто якщо справа полягає в простому додаванні затримки (так що всі необхідні властивості ініціалізуються раніше), тоді використання ледачого - правильний шлях це.

Але якщо вам потрібно постійно змінювати змінну на основі іншої, тоді вам потрібна обчислювана властивість, яка буде працювати в обидві сторони :

  • якщо обчислене властивість встановлено тоді вона встановлює змінні, пов'язані з нею збережені властивості
  • якщо збережені властивості встановлені (або скинуті знову ), це спричинить зміну обчисленої властивості.

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


Хорошим прикладом для використання ледачої властивості може бути те, що коли у вас є firstNameі lastNameтоді ви ліниво створюєте інстанцію fullNameі, ймовірно, ніколи не зміните ім'я firstName lastName вашого об'єкта, fullName - це одноразово ... Або, можливо, щось, що можна зробити лише за lazyвластивостями полягає в тому, що до тих пір, поки ви не отримаєте доступ до властивості, воно ніколи не буде ініціалізовано , тому це зменшить навантаження для ініціалізації вашого класу. Завантаження важкої розрахунку.

Додатково використовуючи сигналlazy волі іншим розробникам: "Гей, спершу прочитайте про інші властивості і зрозумійте, що це таке ... потім приходьте до цієї ледачої властивості ... оскільки значення цього базується на них + це, ймовірно, важке обчислення, до якого не слід звертатися занадто рано ... "

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

В результаті обчислювана властивість додала б додаткові обчислення ... і якщо ваше обчислення дуже просте і не викликається занадто часто, тоді не про що турбуватися, але якщо воно викликається занадто часто або дуже споживає центральний процесор, це може бути краще думати про інші варіанти ...


-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()

Як ця відповідь стосується цього питання?
pkamb
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.