I'm new to swift and I'm trying to create a very simple app but for some reason, the variables are not working.
I'm not really sure what the problem is but I've tried changing var to let.
class ImageViewController: ViewController {
var blocks = [Block(size: 100, centerX: 100, centerY: 100, code: "1", color: image.colorDic["1"]!)]
var codes = ["1"]
var colors = [UIColor(named: "Red")]
//create image
var image = Image(blocksArray: blocks, code: codes, color: colors)
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .blue
title = "Title"
for block in image.blocksArray{
view.addSubview(block.block)
}
// Do any additional setup after loading the view.
}
On the line where I create the image, I get an error that says
"Type 'ImageViewController' has no member 'blocks'"
Also, right under the line where I create 'colors' I'm also getting
"Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffee74cbf48)"
your property image access the block property before they are both initilized, to fix your problem your variable "image" should be flagged as lazy variable, meaning it will be initilized after the other properties have been initilized hence it is safe to access those other properties.
More about lazy properties
The official Swift documentation says,
"Lazy properties are useful when the initial value for a property is dependent on outside factors whose values are not known until after an instance’s initialization is complete."
You can read more about lazy properties here: - Swift Properties Official documentation
Here is how to do it: -
lazy var image: Image = {
return Image(blocksArray: blocks, code: codes, color: colors)
}()
for the case of colors part, I think it is a good practice to avoid typing strings when initializing especially for simple colors like yours and use the enum versions.
Change it to this : -
var colors = [UIColor.red]
or this: -
var colors: [UIColor] = [.red]
Related
my code is:
public var color = [UIColor]()
color.append(String(UIColor(dragInViews[i]!.backgroundColor)))
this code has an error:
Argument labels '(_:)' do not match any available overloads.
I'm trying to resolve problem but I don't know. What is the problem
how to resolve my problem?
You don't need the String() part (nor the UIColor() initializer), it's already a UIColor and the array is defined as an array of UIColor, so just appending it is more than enough.
public var color = [UIColor]()
color.append(dragInViews[i]!.backgroundColor)
Note that the backgroundColor property of a UIView is already a color, so there's no point in instantiating it again.
That particular error you're seeing is because you were trying to instantiate a color with its initializer in this way UIColor(something), but the initializer that exists is UIColor(white:, alpha:) between others. Check out the documentation here.
You need to create new array of UIColor type and in which you need to append color value not string type directly color
var arrColor = [UIColor]()
arrColor.append(UIColor(dragInViews[i]?.backgroundColor ?? UIColor()))
The following is my viewDidAppear() method with which I'm trying to prototype a feature for my app. The graphicsView instance variable is bound via the storyboard to an instance of a subclass of NSView I've written that in turn is enclosed in an NSScrollView within an NSSplitView. This code is within the view controller for that view.
override func viewWillAppear() {
super.viewWillAppear()
let red = CGColor.init(red: 1, green: 0, blue: 0, alpha: 1)
self.view.layer?.backgroundColor = red
let box = NSTextView()
self.graphicsView.addSubview(box)
box.snp.makeConstraints { (make) -> Void in
make.edges.equalTo(self.graphicsView).inset(NSEdgeInsetsMake(100, 100, self.graphicsView.bounds.height - 200, self.graphicsView.bounds.width - 300))
}
box.textStorage?.append(NSAttributedString(string: "Hello Sailor"))
box.alignCenter(self)
}
When executed, I get the error Cannot form weak reference to instance (0x6000001224e0) of class NSTextView. It is possible that this object was over-released, or is in the process of deallocation. along with the usual EXC_BAD_INSTRUCTION fault on the closing bracket of the trailing closure for the constraints.
As far as I can see, the NSTextView will be strongly retained by box, and so I'm at a loss to see the source of the error. The error shows up at the first line of ConstraintItem.init(target: AnyObject?, attributes: ConstraintAttributes). Per the instructions in the readme I'm posting here; can someone on the SnapKit team perhaps shed any additional light on the error? (The app works normally if I remove the box-related code.)
Added information:
The exception happens at line 37 of ConstraintItem.swift, which is self.target = target. I set a breakpoint right before that line and executed e target in the debugger; here's what I got:
(lldb) e target
(AnyObject?) $R1 = (instance_type = 0x0000608000164c80) {
instance_type = 0x0000608000164c80 {
AppKit.NSTextView = {
baseNSText#0 = <extracting data from value failed>
}
title = "some random text"
minimumWidth = 100
}
}
I found several answers.
How you search Google remains important. I varied my searches some more and came upon this here on SO, the short version of which is that it says you can't form a weak reference specifically to NSTextView and includes a link to explanatory Clang documentation.
Perhaps more interestingly, I also found the answer for "erratic" errors I mentioned in the title - one of the machines I develop on turns out to have Swift 3.1, but the other has 3.0.2. The more recent version does not exhibit the error forming the weak link, suggesting Apple has upgraded the NSTextView implementation.
This question already has answers here:
What is "self" used for in Swift?
(10 answers)
Closed 5 years ago.
I read many publications on "self" in Swift and I am starting to get a gist of it, but there is still one thing that is unclear to me.
class Car {
// 1
let make: String
// 2
private(set) var color: String
init() {
make = "Ford"
color = "Black"
}
required init(make: String, color: String) {
self.make = make
self.color = color
}
// 3
func paint(color: String) {
self.color = color
}
}
let car = Car(make: "Tesla", color: "Red")
car.paint("Blue")
I am trying to prove my point with help from the example above.
Several publications that I read indicate that self is used to distinguish 'color' from init() from the 'color' in the parameter from the func paint(color: String).
So when 'self color' is set in the func paint(color: String), which 'color' is it referring to? 'color' from the init() or color from the parameter of func paint(color: String)?
self is a reference to the current instance of the class in which the code is running.
In both the init method and the paint method, it allows you to specify that you wish to set the member variable named color using the value passed in the parameter to the method that is also called color.
The paint method cannot reference the parameter passed to init at all (nor vice versa).
So, in your sample code, both methods set the color of the object to some specified value passed in to the method as a parameter.
The init method sets an initial color for the object.
The paint method then allows you to change the color of the object from that initial color.
This might be clearer if the parameters were simply named differently, e.g.:
required init(initialMake: String, initialColor: String) {
self.make = initialMake
self.color = initialColor
}
func paint(newColor: String) {
self.color = newColor
}
In this case, since the functions are member methods, the self is now entirely optional since the compiler knows that color now can only mean the member called color since there is no other variable or parameter with that name, i.e. the paint method could be written simply as:
func paint(newColor: String) {
color = newColor
}
and this would have the exact same behaviour.
However, some people prefer to keep the self prefix for clarity, even where it isn't strictly required since as well as making the intent clear it can help avoid accidental mistakes if variables or member names are changed.
The self.color in both the init function and the paint function refer to the instance variable on the Car object.
To understand, think of the code without self:
func paint(color: String) {
color = color
}
The function's intent is for the car's color property to be the color passed into it. But with this code, it will not reference the car's color property, and the function wont work as intended. Thus, your function needs a little help in the form of the self keyword.
Also what would be the reason that init() is set up to automatically inititalize the value of "make" and "color" and then require you to initialize on your own after?
You're passing in values with your init function, you're not "automatically initializing the value of "make" and "color". The instance variables you have created for the Car object are non-optional values (Strings), so they cannot be nil, ever. So if you initialize a Car, you have to have their values correctly set after the init function is finished.
self references the object instance. Using self in Swift is optional in most cases.
It's needed in the code you posted because inside the init and the paint methods you have a parameter named color and you also want to access the property named color. Inside the methods, a reference to color will always be to the parameter. So the only way to indicate you want to reference the property named color, you must prefix the reference with self.. If you renamed the parameter so it didn't have the same name as the property, you wouldn't need self.
self.color will always mean a reference to the color property. color will first look for the nearest local variable/parameter of the same name. If found, that's what is used. If not, a property of the same name is used.
I'm trying to add an IBInspectable color to UIView, so that I can set it in the storyboard and later use it in code. In this post regarding UITextField I've seen that I can take advantage of extensions and adding a computed property, but I can't make it work for UIView.
I get a crash: Failed to set (additionalColor1) user defined inspected property on (UIView): [ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key additionalColor1.
Any idea what's causing the crash and how to fix it?
Here's my code:
extension UIView {
#IBInspectable var additionalColor1: UIColor? {
return self.additionalColor1
}
}
For the reference, I'm pasting the code that can be used to set the placeholder color for UITextField (same as the above url). This works ok:
extension UITextField {
#IBInspectable var placeHolderColor: UIColor? {
get {
return self.placeHolderColor
}
set {
self.attributedPlaceholder = NSAttributedString(string: self.placeholder != nil ? self.placeholder! : "", attributes:[NSForegroundColorAttributeName: newValue!])
}
}
}
As mentioned in your question title
Swift extensions can only add computed properties to a type, but they cannot add stored properties.
(For more detailed information please refer to the Extension chapter in The Swift Programming Language.)
The example you posted is actually flawed — even if it has 50 upvotes on Stackoverflow at this time. If you return the value of a property itself from the property's getter you're creating a loop.
#IBInspectable var additionalColor1: UIColor? {
return self.additionalColor1
}
If you have a view and you try to access view.additionalColor1 anywhere in your code your property's getter will be called which returns self.additionalColor1 — or with other words: it returns the property's value again — and guess how? By calling the propery's getter! (And so on...)
The example from the post you mentioned only "works" because the getter is evidently never called. It's only setting that computed property placeHolderColor that changes another stored property, namely the text field's attributedPlaceholder.
So while you can add computed properties to an existing class through an extension you can never think of it as a concrete value that's stored somewhere. Computed properties may only be used to somehow transform the value you assign to it and store the result in existing stored properties.
How is your additionalColor going to be used?
I had to do something similar to this recently, but in my case I was always applying the extra value right away.
For example, I wanted to create a button that looked like a parallelogram. So, I wanted a way to put in a value in the Storyboard, which would apply a CGAffineTransform. I'm not really storing the skew value, just using to change what the thing looks like. Then, in the get, I'm passing back the value from the view's affineTransform routine.
#IBInspectable var skewOffset: Float {
set(newSkewOffset) {
let affineTransform : CGAffineTransform = CGAffineTransform(a: 1.0, b: 0.0, c: CGFloat(newSkewOffset), d: 1.0, tx: 0.0, ty: 0.0)
layer.setAffineTransform(affineTransform)
}
get {
return Float(layer.affineTransform().c)
}
}
So, I'm not storing skewOffset, I'm applying it, and I know how I can look it up later, if I need to get it.
ok so first off I have an achievement Variable that is defined from a struct like so:
struct Achieve {
var node: SKSpriteNode?
var aName:String = ""
var aDes:String = ""
var aImage: String = "" {
didSet {
node?.texture = SKTexture(imageNamed: aImage)
}
}
var aAmount:Int = 0
var aRequired:Int = 0
var aStage:Int = 0
}
var Ach1 = Achieve(node: nil, aName: "Player", aDes: "Games Played", aImage: "locked", aAmount: 0, aRequired: 10, aStage: 1)
My problem is when I try and change the number of the aAmount property it isn't displayed on the SKLabel that displays the amount it just stays at 0 my sklabel is defined in a function like below:
func generateLabels(location: CGPoint, page:SKSpriteNode, text: String) -> SKLabelNode {
let node = SKLabelNode()
node.fontColor = UIColor.whiteColor()
node.fontName = "Helvetica"
node.position = location
node.fontSize = 15
node.text = text
page.addChild(node)
return node
}
func menu() {
_ = generateLabels(CGPointMake(0, -285), page:page1ScrollView, text: "\(Ach1.aAmount) / \(Ach1.aRequired)")
}
It seems to work if I make changes to aAmount when I stop running it and then change it and then run the game again. it also seems to make changes to the aAmount property during gameplay but it doesn't seem to update the label for some reason during the gameplay. Can someone please tell me why it won't update?
Also i'm updating the aAmount property like so:
Ach1.aAmount += 1
print("\(Ach1.Amount)")
In your Achieve struct, add a property called label:
var label: SKLabelNode?
And change the aAmount property to look something like this:
var aAmount: Int = 0 {
didSet {
label?.text = "\(aAmount) / \(aRequired)"
}
}
Now when you generate the labels, replace this:
_ = generateLabels(CGPointMake(0, -285), page:page1ScrollView,
text: "\(Ach1.aAmount) / \(Ach1.aRequired)")
with this
var label = _ = generateLabels(CGPointMake(0, -285), page:page1ScrollView,
text: "\(Ach1.aAmount) / \(Ach1.aRequired)")
Ach1.label = label
Now when you change the aAmount property of Ach1, the label's text should change as well.
Suggestions:
Judging from this and your last question about sprite node images not updating, I think your programming skills are not mature enough. In the long term, you should learn the basics first. Try playing around with UIKit and read more others' code. See how other people do things. You will definitely learn a lot. In the short term though, you shouldn't make each of all these properties binds to a node. That will make Achieve too dependent. What you should do is make Achieve a subclass of SKSpriteNode and add those other sprite nodes and label nodes as sub nodes of the Achieve node.
If I read your code, there isn't any part where you update your SKLabelNode.
If you call the function :
func menu() {
_ = generateLabels(CGPointMake(0, -285), page:page1ScrollView, text: "\(Ach1.aAmount) / \(Ach1.aRequired)")
}
for example to didMoveToView, it generate a label with your aAmount value when you call your scene (so 1 time only as didMoveToView works).
Instead, assuming that you call this menu function many times, it generate everytime a new label but there is no code written from you where you remove the previous label or labels added in past (see page.addChild(node)) and I don't understand why you want to return a label from your function if you don't use it, in fact you assign your result to _. Also you don't explain what is "page1ScrollView", are you using a scrollview?
Anyway, the best mode to update your label is simply to create 1 time only (for example on didMoveToView) and, everytime you want to update it, doing:
myNodeLabel.text = "\(Ach1.aAmount) / \(Ach1.aRequired)"
instead of re-create it everytime. I hope this helps to understand the dynamics that prevent your label to upgrade.
You have to reassign the text of the SKLabel. So when you call the function to change the number (or whatever it is), also run the code to make the label what you want it to be. My guess as to why it only works sometimes is that sometimes you call it in the update (or somewhere else that gets called repeatedly) and sometimes you call it somewhere like didMoveToView (only called once, so label doesn't get updated because of the way your code is).
Just whenever you update aAmount, add this code after it:
label.text = "\(aAmount)"