xcode - cgrectgetwidth - cgrectmake swift 4
Cómo inicializar propiedades que dependen unas de otras (4)
Quiero que una imagen se mueva hacia abajo. Si presiono un botón, la imagen se moverá hacia abajo en 1.
Agregué la imagen y un botón:
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)
}
Al menos dije en la función "fahren" para mover la imagen hacia abajo por 1.
func fahren(){
corY += 1
panzer.frame = CGRectMake(corX, corY, 30, 40) //
self.view.addSubview(panzer);
}
Entonces mi problema es: recibo varios errores con estos corX y corY. Sin ellos, funciona perfectamente, pero es como un botón de un solo uso. Los errores son: ViewController.Type no tiene un miembro llamado corX y ViewController.Type no tiene un nombre de miembro panzer Donde obtengo los errores que hice // para mostrar en qué líneas.
PD: uso Xcode Beta5
Aquí está el código completo sin nada más:
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);
}
}
@MartinR ha señalado el problema principal aquí:
var corX = 0
var corY = 0
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40))
El problema es que un inicializador predeterminado de Swift no puede hacer referencia al valor de otra propiedad, porque en el momento de la inicialización, la propiedad aún no existe (porque la instancia en sí misma aún no existe). Básicamente, en el inicializador por defecto de panzer
te estás refiriendo implícitamente a self.corX
y self.corY
, pero no existe un self
porque el self
es exactamente lo que estamos creando.
Una solución es hacer que el inicializador sea flojo:
class ViewController: UIViewController {
var corX : CGFloat = 0
var corY : CGFloat = 0
lazy var panzer : UIImageView = UIImageView(frame: CGRectMake(self.corX, self.corY, 30, 40))
// ...
}
Eso es legal porque panzer
no se inicializa hasta más adelante, cuando su código actual lo menciona por primera vez. En ese momento, el self
y sus propiedades existen.
Me dirijo al título de la pregunta:
Las propiedades tanto perezosas como calculadas lo ayudan a lidiar cuando el valor inicial de una propiedad no se conoce hasta después de que el objeto se inicializa. Pero hay algunas diferencias. He resaltado las diferencias con negrita.
Si simplemente necesita inicializar una variable después de que se inicialicen otras variables, deberá usar lazy
es decir, si el punto es simplemente agregar un retraso (para que todas las propiedades requeridas se inicialicen antes), entonces usar perezoso es el camino correcto a seguir. para ello.
Pero si necesita cambiar constantemente una variable en función de otra, entonces necesita una propiedad calculada que funcione en ambos sentidos :
- si la propiedad calculada establece entonces establece las variables sus propiedades almacenadas relacionadas
- si las propiedades almacenadas están configuradas (o se restablecen nuevamente ), se activará un cambio en la propiedad calculada.
si cambia el valor del inmueble perezoso, no afectará a las propiedades históricas en las que se basó. ver here
Un buen ejemplo para usar una propiedad perezosa sería que una vez que tenga firstName
y lastName
entonces instanciaría perezosamente un fullName
y probablemente nunca cambiaría el firstName lastName de su objeto, su fullName es solo una vez ... O quizás algo que pueda Solo lo harán las propiedades lazy
, es que hasta que no acceda a la propiedad , nunca se inicializará , por lo tanto, esto disminuiría la carga de inicialización de su clase. Cargando un cálculo pesado
Además, usar el lazy
indicará a otros desarrolladores: "Oye, primero lee sobre las otras propiedades y entiende cuáles son ... luego ve a esta propiedad perezosa ... ya que el valor de esto se basa en ellas + es probable que cálculo pesado que no se debe acceder demasiado pronto ... "
En cuanto a la propiedad calculada, un buen ejemplo sería si configuras la temperatura en Fahrenheit, luego también deseas que tu temperatura centígrafa cambie su valor ... y si estableces la temperatura celsius , entonces de nuevo quieres cambiar tu valor de Fahrenheit .
Como resultado, la propiedad calculada agregaría cómputo adicional ... y si su cálculo es muy simple y no se llama con demasiada frecuencia, entonces no es nada de qué preocuparse, pero si se llama con demasiada frecuencia o consume mucha CPU, podría ser mejor. pensar en otras opciones ...
Su propiedad dependiente debe ser:
-
lazy
- Tener un explícito
: Type
- Usa el
self.
para acceder a otras propiedades
Ejemplo:
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''
//
// 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()