Hey guys i am using the xcode6 beta 4.
Im trying to to give a button a size with the argument .frame but i got an expected declaration error. I hope you can tell me whats wrong with my code and why i get this error!
var button1 = UIButton.buttonWithType(UIButtonType.System) as UIButton
button1.frame = CGRectMake(100, 100, 40, 145) //Expected declaration
button1.addTarget(self, action: "Action:", forControlEvents:UIControlEvents.TouchUpInside)
menuView.addSubview(button)
You might be mistaken as to which line is throwing the error.
Look at line 4 in what you shared.
menuView.addSubview(button)
Shouldn't that be button1, not button?
menuView.addSubview(button1)
EDIT
As mentioned in my comment, you might be trying to access the button1 declaration inside the class declaration, all logic must be within functions. Only variable declarations can be outside of functions.
class Foo {
var menuView = UIView()
var button1: UIButton = UIButton.buttonWithType(UIButtonType.System) as UIButton
button1.frame = CGRectMake(100, 100, 40, 145)
button1.addTarget(self, action: "Action:", forControlEvents:UIControlEvents.TouchUpInside)
menuView.addSubview(button1)
}
The above is not ok, you can declare the button1 variable at the class level but you cannot start accessing it until you're inside a function like below.
class Foo {
var menuView = UIView()
var button1: UIButton = UIButton.buttonWithType(UIButtonType.System) as UIButton
func setupButton1() {
button1.frame = CGRectMake(100, 100, 40, 145)
button1.addTarget(self, action: "Action:", forControlEvents:UIControlEvents.TouchUpInside)
menuView.addSubview(button1)
}
}
Related
I want a picture to move to the bottom. If I press a button the pic should move down by 1.
I added the picture and a button:
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)
}
At least I said in function "fahren" to move the picture down by 1.
func fahren(){
corY += 1
panzer.frame = CGRectMake(corX, corY, 30, 40) //
self.view.addSubview(panzer);
}
So my problem is: I get several errors with these corX and corY thing. Without them it works perfectly but than its like a single-use button. The errors are: ViewController.Type does not have a member named corX and ViewController.Type does not have a member names panzer Where I get the errors I made // to show in which lines.
PS: I use Xcode Beta5
Here's the complete code without anything else:
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 has pointed out the major issue here:
var corX = 0
var corY = 0
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40))
The problem is that a Swift default initializer cannot refer to the value of another property, because at the time of initialization, the property doesn't exist yet (because the instance itself doesn't exist yet). Basically, in panzer's default initializer you are implicitly referring to self.corX and self.corY - but there is no self because self is exactly what we are in the middle of creating.
One workaround is to make the initializer lazy:
class ViewController: UIViewController {
var corX : CGFloat = 0
var corY : CGFloat = 0
lazy var panzer : UIImageView = UIImageView(frame: CGRectMake(self.corX, self.corY, 30, 40))
// ...
}
That's legal because panzer doesn't get initialized until later, when it is first referred to by your actual code. By that time, self and its properties exist.
Your dependent property needs to be:
lazy
Have an explicit : Type
Use self. to access other properties
Example:
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'
I'm addressing the title of the question:
Both lazy and computed properties help you deal with when the initial value for a property is not known until after the object is initialized. But there are some differences. I've highlighted the differences with bold.
If you simply need to initialize a variable after some other variable(s) is initialized then you should use lazy ie if the point is to simply add a delay (so all required properties get initialized before) then using lazy is the right way to go for it.
But if you need to constantly change a variable based on another, then you need a computed property that would work both ways:
if the computed property set then it sets the variables its related stored properties
if the stored properties are set (or are reset again) then it will trigger a change in then computed property.
if you change the lazy property's value it won't affect the storied properties that it was based on. see here
A good example for using a lazy property would be that once you have firstName & lastName then you would lazily instantiate a fullName and likely you would never change the firstName lastName of your object your fullName is a onetime only...
Or perhaps something that can only be done by lazy properties is that up until you don't access the property it won't ever get initialized, therefore this would decrease the initialization load of your class. Loading a heavy calculation.
Additionally using the lazy will signal to other developers: "Hey first go read about the other properties and understand what they are...then come to this lazy property...since the value of this is based on them + this is likely a heavy computation that shouldn't be accessed too early..."
As for computed property a good example would be if you set the temperature to Fahrenheit then you also want your celsius temperature to change its value...and if you set the celsius temperature then again you want to change your Fahrenheit value.
As a result computed property would add extra computation...and if your computation is very simple and isn't called too frequently then it's nothing to worry about but if it get's called too often or is very CPU-consuming then it might be better to think of other options...
//
// 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()
I have 2 UIViewControllers, one starting the GameScene and the other is supposed to show the highscore, as below. The problem is that my second UIViewController does not want to show the udpated highscore. It just keeps on showing 0 all the time.
Does anyone know what the problem is? I tried using viewDidLoad as well as viewWillAppear but none of them work.
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 21))
label.center = CGPoint(x: 160, y: 285)
label.textAlignment = .center
label.text = String(GameScene().highScore)
self.view.addSubview(label)
}
in the GameScene, I have declared the highScore as so:
var highScore = Int()
and used in a gameOver function as so:
if score > highScore {highScore = score}
thanks
label.text = String(GameScene().highScore)
This piece of line suggests GameScene is a static class. Un less it is, you simply cannot access variables in such a manner.
You will need to pass data between view controllers to pass data between the scenes. You can pass data using one of three options:
Though performSegue()
performSegueWithIdentifier() and make a connection in IB
make a connection in IB and define an action
I want a picture to move to the bottom. If I press a button the pic should move down by 1.
I added the picture and a button:
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)
}
At least I said in function "fahren" to move the picture down by 1.
func fahren(){
corY += 1
panzer.frame = CGRectMake(corX, corY, 30, 40) //
self.view.addSubview(panzer);
}
So my problem is: I get several errors with these corX and corY thing. Without them it works perfectly but than its like a single-use button. The errors are: ViewController.Type does not have a member named corX and ViewController.Type does not have a member names panzer Where I get the errors I made // to show in which lines.
PS: I use Xcode Beta5
Here's the complete code without anything else:
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 has pointed out the major issue here:
var corX = 0
var corY = 0
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40))
The problem is that a Swift default initializer cannot refer to the value of another property, because at the time of initialization, the property doesn't exist yet (because the instance itself doesn't exist yet). Basically, in panzer's default initializer you are implicitly referring to self.corX and self.corY - but there is no self because self is exactly what we are in the middle of creating.
One workaround is to make the initializer lazy:
class ViewController: UIViewController {
var corX : CGFloat = 0
var corY : CGFloat = 0
lazy var panzer : UIImageView = UIImageView(frame: CGRectMake(self.corX, self.corY, 30, 40))
// ...
}
That's legal because panzer doesn't get initialized until later, when it is first referred to by your actual code. By that time, self and its properties exist.
Your dependent property needs to be:
lazy
Have an explicit : Type
Use self. to access other properties
Example:
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'
I'm addressing the title of the question:
Both lazy and computed properties help you deal with when the initial value for a property is not known until after the object is initialized. But there are some differences. I've highlighted the differences with bold.
If you simply need to initialize a variable after some other variable(s) is initialized then you should use lazy ie if the point is to simply add a delay (so all required properties get initialized before) then using lazy is the right way to go for it.
But if you need to constantly change a variable based on another, then you need a computed property that would work both ways:
if the computed property set then it sets the variables its related stored properties
if the stored properties are set (or are reset again) then it will trigger a change in then computed property.
if you change the lazy property's value it won't affect the storied properties that it was based on. see here
A good example for using a lazy property would be that once you have firstName & lastName then you would lazily instantiate a fullName and likely you would never change the firstName lastName of your object your fullName is a onetime only...
Or perhaps something that can only be done by lazy properties is that up until you don't access the property it won't ever get initialized, therefore this would decrease the initialization load of your class. Loading a heavy calculation.
Additionally using the lazy will signal to other developers: "Hey first go read about the other properties and understand what they are...then come to this lazy property...since the value of this is based on them + this is likely a heavy computation that shouldn't be accessed too early..."
As for computed property a good example would be if you set the temperature to Fahrenheit then you also want your celsius temperature to change its value...and if you set the celsius temperature then again you want to change your Fahrenheit value.
As a result computed property would add extra computation...and if your computation is very simple and isn't called too frequently then it's nothing to worry about but if it get's called too often or is very CPU-consuming then it might be better to think of other options...
//
// 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()
I've got an NSButton in a View Controller that, when clicked, should call a method in an instance of another class (I have that instance in the View Controller). However, the action method is never called.
My code is below (it's short and simple). Please can somebody explain why this is?
View Controller class with the button:
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let b:NSButton = NSButton(frame: NSRect(x: 150, y: 200, width: 30, height: 30))
self.view.addSubview(b)
let g = Global()
b.target = g
b.action = #selector(g.s)
}
}
Class called 'Global' that I create an instance of, that the button should then call a method within:
class Global:NSObject {
override init() {
super.init()
}
#objc dynamic func s() {
Swift.print("S ran")
}
}
Thanks
Update: For easy reproduction, I've created a GitHub repo showing the issue in its simplest form here.
The problem is that by the time you click the button, target has been set to nil. This is because g is stored as a local variable and target is a weak property, so after viewDidLoad is finished, g is released and the target becomes nil. So, by the time you click the button, there is no object on which to call the action.
You need to store a strong reference to the target somewhere. One way would be to store it as an instance variable on your view controller:
class ViewController: NSViewController {
let g = Global()
override func viewDidLoad() {
super.viewDidLoad()
let b:NSButton = NSButton(frame: NSRect(x: 150, y: 200, width: 30, height: 30))
self.view.addSubview(b)
b.target = g
b.action = #selector(g.s)
}
}
I am organizing a code an app that iPad Air 2 I use to control finances of a company (as a developer without AppStore). This app displays painels with information, and these panels are displayed and hidden as the user action.
I decided to try to create different ways and separate my schedule between arquivos.swift.
That's my ViewController. Only two buttons, it's all I do for Interface Builder (for example). The rest of the application is created programmatically.
This ViewController is connected directly to the ViewController class:
class ViewController: UIViewController {
override func prefersStatusBarHidden() -> Bool {
return true
}
var panelInfo: PanelInfo!
var selfSize: CGSize {
get {
return self.view.frame.size
}
}
override func viewDidLoad() {
super.viewDidLoad()
panelInfo = PanelInfo(viewControllerSize: selfSize)
self.view.addSubview(panelInfo.panel)
}
#IBAction func showPropNavigationbar(sender: AnyObject) {
panelInfo.show()
}
#IBAction func hidePropNavigationbar(sender: AnyObject) {
panelInfo.hide()
}
}
To create a view that will be displayed where the information, I created a class that manages it.
class PanelInfo {
/*** View Controller Size ***/
private var viewSize: CGSize!
/*** View ***/
internal var panel: UIView!
/*** View Panel Size ***/
private var size: CGSize!
/*** View Position ***/
private var originShow: CGPoint!
private var originHide: CGPoint!
private var closeButton: UIButton!
/*** Start the instance ***/
init(viewControllerSize: CGSize) {
/*** Set values in variables above ***/
viewSize = viewControllerSize
size = CGSize(width: 300, height: viewSize.height)
originShow = CGPoint(x: viewSize.width - size.width, y: 0)
originHide = CGPoint(x: viewSize.width, y: 0)
/*** Create the panel view ***/
panel = UIView(frame: CGRect(origin: originHide, size: self.size))
panel.backgroundColor = UIColor(red: 0.014, green: 0.106, blue: 0.179, alpha: 1.000)
/*** Create the closeButton ***/
closeButton = UIButton(type: .System)
closeButton.frame = CGRect(origin: CGPoint(x: 8, y: 8), size: CGSize(width: 32, height: 32))
closeButton.setImage(UIImage(named: "closeButton"), forState: .Normal)
panel.addSubview(closeButton)
}
func hidepanel(sender: AnyObject) {
print(sender)
self.hide()
}
/*** Show panel in ViewController ***/
internal func show() -> Void {
UIView.animateWithDuration(0.3) { () -> Void in
self.panel.frame.origin = self.originShow
}
}
/*** Hide panel in ViewController ***/
internal func hide() -> Void {
UIView.animateWithDuration(0.3) { () -> Void in
self.panel.frame.origin = self.originHide
}
}
}
When the code is executed, and I touch in 'Show', the panel appears. Working perfectly.
When I touch in 'Hide', the panel disappears. Working perfectly.
The Problem
I'm trying to create a button for programming and add a target to run make the panel close. This button is closeButton.
If i added the target (nil, action: "hidepanel:", forControlEvents: .TouchUpInside
Nothing happens
If I change for closeButton.addTarget(nil, action: "hidepanel:", forControlEvents: .TouchUpInside
Nothing happens
If I change for closeButton.addTarget(self, action: "hidepanel:", forControlEvents: .TouchUpInside)
The error appear:
*** NSForwarding: warning: object 0x7a99ee00 of class 'Panel_Model.PanelInfo' does not implement methodSignatureForSelector: -- trouble ahead
Unrecognized selector -[Panel_Model.PanelInfo hide panel:]
If I try change for closeButton.addTarget(self, action: "hidepanel", forControlEvents: .TouchUpInside)
The previous error appears
Note: This is a example. The app original is bigger. When I started to build the app, had little time to do it and had little experience in Swift (and Obj-C as well), so I created everything needed in a ViewController. Today I am with almost 5000 lines of programming, and need to find a better way to organize.
I believe that the error can be related to (target: AnyObject?, ...) , the first item of AddTarget, for the reason of being used in a class without superclass... But I can't find a way to solve it.
My question:
1º This is the best form of organization?
2º Knowing that I am using a class, and this class have a variabel controlling a UIView. Can I implement added buttons to view and set targets on the buttons?
3º If possible, what is the best way to do this, could show examples?
Try the following
let button:UIButton = UIButton(frame: CGRect(x: 0, y: 0, width: 70, height: 30))
button.addTarget(self, action: Selector("hidepanel:"), forControlEvents: UIControlEvents.TouchUpInside)
Update
Try inheriting PanelInfo from `NSObject.