I am absolutely new in swift and try to find a way for changing the color of labels randomly
for i in 1...20 {
let label = [label1, label2, label3]
let a = label.randomElement()
a.textColor = UIColor.orange // there is the problem
Any ideas?
Collection method randomElement() returns an optional. If your collection element is already optional you have a double optional. You need to use optional chaining or unwrap your optional.
a?.textColor = .orange // if your `a` label is of type `UILabel?` use a single exclamation mark
Related
Whenever we need to du something with an optional value in swift we need to unwrap it to operate, not on the optional, but on the value ”inside” the optional.
var optionalString:String? = "Hello"
optionalString!.append(" World!")
Note the exclamation mark on the second line.
But when using the optional type cast operator (as?) on an optional value, one does not need to unwrap the optional, we just provide it with the optional itself, and it just magically works.
let sneakyString:Any? = "Hello!"
let notSoSneakyString = sneakyString as? String
Note the absent exclamation mark on the second line.
The magic is a bit more obvious if we spell it out:
let sneakyString:Any? = Optional("Hello")
let notSoSneakyString = sneakyString as? String
Its not a string we're trying to cast but an enum with a string as an associated value.
I would expect that I would have to do this:
let sneakyString:Any? = "Hello!"
let notSoSneakyString = sneakyString! as? String
Note the exclamation mark on the second line.
Does the type cast operators act on optionals and non optionals in the same way?
The as? operator makes a cast if it possibly can, and returns either the casted object or nil. It handles nil values too, so that if sneakyString were nil, you wouldn't have been able to cast it to a String and the cast would have failed. Same behaviour as if it were non-nil but not castable to String.
In other words, you don't need the ! because as? handles nil values automatically itself.
I am playing with Arrays in playground and I am bit confused. Here is code:
var players = ["tob", "cindy", "mindy"] //["tob", "cindy", "mindy"]
print(players.isEmpty) // False
var currentPlayer = players.first // "tob"
print(currentPlayer) // "Optional("tob")\n"
Why does it says "Optional"?
I found explanation: "The property first actually returns an optional, because if the array were empty, first would return nil."
But it is not empty. .isEmpty //false, So I am not understanding this.
Thanks for help in advance.
The correct way to think of Optional is that this may or may not have a value. What is the first element of an empty list? There is no such thing. It is not a value. We call that lack of a value nil or .None.
In Swift a variable must have a specific type. So your example:
let currentPlayer = players.first
What is the type of currentPlayer? It may be a String, or it may be nothing at all. It is a "maybe string" and in Swift that's called an Optional<String>. Whether players has elements or doesn't have elements doesn't change the type of currentPlayer.
If you want to do something if-and-only-if the variable has a value, then there are many ways. The simplest is if-let.
let players = ["tob", "cindy", "mindy"] //["tob", "cindy", "mindy"]
print(players.isEmpty) // False
if let currentPlayer = players.first {
print(currentPlayer)
}
This will print tob as you're expecting.
Another very common approach is the guard let
let players = ["tob", "cindy", "mindy"] //["tob", "cindy", "mindy"]
guard let currentPlayer = players.first else { return }
print(currentPlayer)
This lets you avoid nesting the rest of your function inside of curly braces, but otherwise is the same approach.
It is possible to convert an Optional into its underlying type using !, but this is very dangerous and should be avoided except where absolutely necessary. Tools like if-let and guard-let (and also Optional.map) are almost always preferred.
But the key here is to understand that all Swift variables have a single type, and sometimes that type is "maybe it has a value, maybe it doesn't."
If we look at the description of first, we will see that it always returns optional type:
public var first: Self.Generator.Element? { get }
I have a Player class which has an enum for changing the Player type. See init below.
init(playerType: PlayerType) {
self.playerType = playerType
spriteTexture = SKTexture(imageNamed: playerType.simpleDescription())
sprite = SKSpriteNode(texture: spriteTexture)
sprite.name = playerCategoryName
sprite.zPosition = 10
}
In the GameScene I change some player properties using the following method, but the enum value for player.playerType doesn't change when assigned. What is cause and how can I correct this?
func changePlayer (newPlayerType:Player.PlayerType) {
player.spriteTexture = SKTexture(imageNamed: newPlayerType.simpleDescription())
let action = SKAction.setTexture(player.spriteTexture)
player.sprite.runAction(action)
player.playerType == newPlayerType //this doesn't seem to work, player.playerType remains unchanged every time
println("raw value of newPlayerType is \(newPlayerType.rawValue)")
println("raw value of player.playerType is \(player.playerType.rawValue)")
}
You cannot assign a different case to an existing enum; you can make an existing enum change its own case, but not by assigning into it.
You can replace an enum instance (with one case) by another enum instance (with a different case) of the same enum, but only if the enum is a var reference, not a let reference.
== is the equality operator.
You're looking for = for assignment (note, only one equals sign).
Check your enum reference it should be var not let
What is the difference between:
var title:String? = "Title" //1
var title:String! = "Title" //2
var title:String = "Title" //3
What am I saying if I were to set title in each way and am I forced to unwrap each variable in a different way?
Think about ? and ! like a box that might have a value or not.
I recommend this article.
Optional box that might have value or might not, and that optional box is not unwrapped.
var title:String? = "Title" //second and third picture
You use unwrapped value like that:
if let title = title {
//do sth with title, here is the same like let title: String = "Title"
}
Optional box that might have a value or might not, and that optional box is actually unwrapped. If there is a value and you access that value, that is ok (second image, just replace ? with !), but if there is no value, then app crash (third image, just replace ? with !)
var title:String! = "Title"
That variable have a value for sure, and you cannot assign to this value nil (because it is not optional). Optional means that there is a value or there is no value (nil):
var title:String = "Title" //first picture
`var title:String? = "Title"`
title currently has a value of Title, but in the future it could possibly be nil. I will need to unwrap it using optional binding:
if let unwrappedTitle = title {
println(unwrappedTitle)
}
Or by forcing the unwrap with the ! character
let unwrappedTitle = title!
The above will crash if title is nil
`var title:String! = "Title"`
title currently has a value of "Title". It could possibly be nil, but I know that it never will be when I am using it. You don't need to unwrap this with optional binding or by forcing the unwrap with the ! character.
Your program will crash if this value is ever accessed while nil, but the compiler will let you set this value to nil.
`var title:String = "Title"`
title currently has a value of "Title". This may change, but the variable title will always have some string value. I don't need to check for nil with optional binding or by forcing an unwrap. The compiler will not let you build if you try to set title to nil.
var title:String? = "Title" //1 Nil Possible-Force Unwrap needed-Nil checking when use
var title1:String! = "Title"//2 Nil Possible-Force Unwrap not needed-Nil cheking when Use
var title2:String = "Title" //3 Nil Not possible as its initailize when declared-No force unwrap needed-No Nil Cheking is needed.
Can someone please explain why if, as it says in the import definition:
typealias SKColor = UIColor
I get the error 'UIColor!' is not identical to 'SKColor' when I do the following? I was about to say 'I know the difference between UIColor and UIColor!' but actually, maybe I don't truly understand!
import UIKit
import SpriteKit
func nColours(gradient: [SKColor]) -> Int {
return gradient.count
}
let gradient = [SKColor.redColor(), SKColor.magentaColor()]
nColours([SKColor.redColor(), SKColor.magentaColor()]) // 2, OK
nColours(gradient) // <<<<<< Error
// 'UIColor!' is not identical to 'SKColor'
experimenting, I tried this :
let gradient = [SKColor.redColor(), SKColor.magentaColor()]
let b = (gradient == [SKColor.redColor(), SKColor.magentaColor()]) // <<<<<< Error
// '[UIColor!]' is not convertible to '_ArrayCastKind'
The problem is that UIColor and UIColor! are technically different types - the ! stands for Implicitly Unwrapped Optional and is used where there is an optional value, but that optional should always have a value.
It seems that many objects returned from the system frameworks use implicitly optional types, though this usage should get less common as the frameworks are fully converted to Swift - there's no reason UIColor.redColor() would ever return a nil color, so its return type will probably change from SKColor! to SKColor in the future.
In your code example, if you change your let gradient declaration to explicitly declare the array as being of type [SKColor] (as opposed to SKColor!) the compiler happily carries on:
let gradient : [SKColor] = [SKColor.redColor(), SKColor.magentaColor()]