In Objective C this is a valid line of code
self.scrollView.contentSize = self.image ? self.image.size : CGSizeZero;
Which is checking if self.image is nil or not, and choosing the left or right value.
In Swift I want to recreate the same line of code. Actually it should be exactly the same except without the semicolon
self.scrollView.contentSize = self.image ? self.image.size : CGSizeZero
But this is not valid in my Swift code getting an error, 'UIImage does not conform to protocol LogicValue'
What is the correct Swift code?
This code works if self.image is an Optional. There is no reason to have it otherwise because self.image literally cannot be nil.
The following code is completely valid:
var image : UIImage?
self.scrollView.contentSize = image ? image!.size : CGSizeZero
Note: you must use the "!" to "unwrap" the optional variable image so that you can access its size. This is safe because you just tested before hand that it is not nil.
This would also work if image is an implicitly unwrapped optional:
var image : UIImage!
self.scrollView.contentSize = image ? image.size : CGSizeZero
You are describing the conditional assignment ternary operator ?:, which operates as so:
(condition) ? (assign this value if the condition evaluates to true) : (assign this value if the condition evaluates to false)
therefore, self.image needs to be something that evaluates to true or false, which is the case in Swift for anything that conforms to the LogicValue protocol
unlike Obj-C where the mere presence of an object is equivalent to true, Swift requires a little more... we are given Optional Values that can be used as conditionals!
so what you described works if self.image is an optional value, which it sounds like it is not if you are seeing that error
to round out the answer:
if self.image is not optional, it can never be nil so you can safely make the assignment without doing the conditional check
if it is possible at some point that self.image may be nil, then it needs to be one of the optional types, either UIImage? or UIImage! (implicitly unwrapped optional, see https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html)
Related
I'm learning Swift and some iOS development. Here is my code which I'm trying to figure out why XCode is complaining.
private func rollMyDice() {
let diceRollImages = [
UIImage(named: "DiceOne"),
UIImage(named: "DiceTwo"),
UIImage(named: "DiceThree"),
UIImage(named: "DiceFour"),
UIImage(named: "DiceFive"),
UIImage(named: "DiceSix")
]
diceOneImageView.image = diceRollImages.randomElement()
diceTwoImageView.image = diceRollImages.randomElement()
}
So in this case, diceOneImageView.image = diceRollImages.randomElement() will complain that it cannot assign UIImage?? to UIImage?.
ImageView.image is of type UIImage?. What I don't understand is why is randomElement() here is returning UIImage??.
What does UIImage?? mean? I thought ?? was the nil coalescsing operator, so I'm not sure why it's part of some return type.
Also reading the documentation on randomElement(), it should return ?. So in this case, I would expect diceRollImages.randomElement() to return UIImage? which should suit diceOneImageView.image.
What is happening here? I know I can fix it by using ! or using nil coalescing etc. to make it work. Just don't get what's going on.
Note that UIImage(named:) is a failable initialiser, and so the expressions UIImage(named: "DiceOne") etc are of type UIImage?. This makes the array diceRollImages of type [UIImage?]. Each Element of the array is UIImage?, alternatively written as Optional<UIImage>.
As you may know, Optional is just an enum with two cases, .some and .none (aka nil). Each element of the array can be:
.some(image), if a UIImage is created successfully from the name
.none, if there is no images with that name
randomElement is declared to return Element? (Optional<Element>), because the array could have no elements, and hence, cannot give you a random element. randomElement returns:
.none, when the array is empty
.some(elementOfTheArray), when the array is non-empty and elementOfTheArray is a random element in array.
Recall that Element is Optional<UIImage> in the case of diceRollImages, and that the array elements (elementOfTheArray) could have values .some(image) or .none.
Therefore, we can say that randomElement returns a value of type Optional<Optional<UIImage>>, aka UIImage??, and it could be one of 3 things:
.none when the array is empty
.some(.some(image)) when the array is non-empty and a successfully-created image in the array is randomly selected
.some(.none) when the array is non-empty and a unsuccessfully-created image in the array is random selected
Since you are hardcoding the array of images, you know that the array is not empty, and so it is safe to force-unwrap the outer layer of the optional:
diceOneImageView.image = diceRollImages.randomElement()!
The answer has been given already in the comments: UIImage(named:) returns (optional) UIImage? and the result of calling randomElement() on an optional is a double optional ??
This is a good example where force unwrapping is welcome.
The images are part of the application bundle which is immutable at runtime and the app is useless if one of them is missing.
Declare diceRollImages
let diceRollImages = [
UIImage(named: "DiceOne")!,
UIImage(named: "DiceTwo")!,
UIImage(named: "DiceThree")!,
UIImage(named: "DiceFour")!,
UIImage(named: "DiceFive")!,
UIImage(named: "DiceSix")!
]
If the code crashes nevertheless it reveals a design mistake which can be fixed immediately.
You can even force unwrap randomElement()! because the array is a constant and is clearly not empty.
This question already has answers here:
Why would I use if and let together, instead of just checking if the original variable is nil? (Swift)
(2 answers)
Closed 7 years ago.
Apple has this segment of code on one of their sample projects:
let existingImage = cache.objectForKey(documentIdentifier) as? UIImage
if let existingImage = existingImage where cleanThumbnailDocumentIDs.contains(documentIdentifier) {
return existingImage
}
why is apple using this if let? Isn't more logical to simply use
if cleanThumbnailDocumentIDs.contains(documentIdentifier) {
return existingImage!
}
???!!
If you use
let existingImage = cache.objectForKey(documentIdentifier) as? UIImage
if let existingImage = existingImage where cleanThumbnailDocumentIDs.contains(documentIdentifier) {
return existingImage
}
This will make sure that if existingImage == nil,it will not
execute return existingImage.
Besides,if let also unwrap existingImage from UIImage? to
UIImage
As Abhinav mentioned above, Apple introduced a new type called optional type with Swift.
What does optional mean?
Short and Sweet, "Optional types are types, which can contain a value of a particular data type or nil".
You can read more about optionals and their advantages here : swift-optionals-made-simple
Now whenever you want to make use of value present in an optional type, first you need to check what it contains i.e. does it contains a proper value or it contains nil. This process is called optional unwrapping.
Now there are two types of unwrapping,
Forced unwrapping : If you're sure that an optional will have an value all the time, you can then unwrap the value present in the optional type using "!" mark. This is force unwrapping.
The one more way is to use if let expression, this is safe unwrapping, here you'll check in your program that, if optional has a value you will do something with it; if it doesn't contain value you'd do something else. A simple example is this (You can test this in play ground:
func printUnwrappedOptional (opt:String?) {
if let optionalValue = opt { //here we try to assign opt value to optionalValue constant, if assignment is successful control enters if block
println(optionalValue) // This will be executed only if optionalValue had some value
}
else {
println("nil")
}}
var str1:String? = "Hello World" //Declaring an optional type of string and assigning it with a value
var str2:String? //Declaring an optional type of string and not assigning any value, it defaults to nil
printUnwrappedOptional(str1) // prints "Hello World"
printUnwrappedOptional(str2) // prints "nil"
Hope this clears your question, read through the link given above it'll be more clear to you. Hope this helps. :)
Edit: In Swift 2.0, Apple introduced "guard" statements, once you're good with optionals go through this link, guard statement in swift 2. This is another way to deal with optionals.
Using if let, makes sure that the object (existingImage) is not nil, and it unwraps it automatically, so you are sure inside the if that the condition is true, and the object is not nil, and you can use it without unwrap it !
With Swift, Apple has introduced a new concept/type - Optional Type. I think you better go through Apple Documentation.
Swift also introduces optional types, which handle the absence of a
value. Optionals say either “there is a value, and it equals x” or
“there isn’t a value at all”. Optionals are similar to using nil with
pointers in Objective-C, but they work for any type, not just classes.
Optionals are safer and more expressive than nil pointers in
Objective-C and are at the heart of many of Swift’s most powerful
features.
existingImage is an optional (as? UIImage) and therefor needs to be unwrapped before used, otherwise there would be a compiler error. What you are doing is called forced unwrapping via !. Your program will crash, if existingImage == nil and is therefor only viable, if you are absolutely sure, that existingImage can't be nil
if let and optional types is more help where is there is changes to get nil values to void crashes and unwanted code executions.
In Swift 2.0,
guard
will help us lot where our intention is clear not to execute the rest of the code if that particular condition is not satisfied
So I set up a NSUserDefault to store a string in my GameViewController
NSUserDefaults.standardUserDefaults().setObject("_1", forKey: "SkinSuffix")
The idea is it stores a suffix which I will attach to the end of an image name in order to save what skin of a character the player should use.
When I call the value in my GameScene class like so
var SkinSuffix = NSUserDefaults.standardUserDefaults().stringForKey("SkinSuffix")
println(SkinSuffix)
it prints "Optional("_1")" instead of just "_1" so when I try to change the name of my image file like so, it doesn't load the image file
hero = SKSpriteNode(texture: heroAtlas.textureNamed("10Xmini_wizard\(SkinSuffix)"))
How do I fix this issue?
You can unwrap the String using the Optional Binding construct. This avoids a crash of the app if the value is nil.
if let skinSuffix = NSUserDefaults.standardUserDefaults().stringForKey("SkinSuffix") {
println(skinSuffix)
}
Update: As correctly suggested in the comment below, I am putting the retrieved value in a constant (let). We should always use constants when we don't need to change the value. This way the Swift compiler can make some optimizations and does prevent us from changing that value.
That's because it's implicitly an optional not of type String. You need to case it as such or unwrap the optional in your println statement.
var SkinSuffix = NSUserDefaults.standardUserDefaults().stringForKey("SkinSuffix") as! String
Or in your println: println(SkinSuffix!)
As a side note, you should you camelCase for your variable names.
You can use "??" Nil Coalescing Operator to help you dealing with nil and it also
allows you to specify a default string value.
NSUserDefaults().setObject("_1", forKey: "SkinSuffix")
let skinSuffix = NSUserDefaults().stringForKey("SkinSuffix") ?? ""
println(skinSuffix) // "_1"
How do I check to see if a UIImage is empty?
class UserData {
...
var photo: UIImage = UIImage()
}
My ViewController code looks like:
var userData = UserData()
...
func preparePhoto(){
if (self.userData.photo == nil) {
...
}else{
...
}
}
self.userData.photo == nil won't work in Swift.
Xcode says: UIImage is not convertible to MirrorDisposition
self.userData.photo will never be nil, so the question is pointless.
The reason is that you have declared photo as a UIImage. That is not the same as a UIImage? - an Optional wrapping a UIImage. Only an Optional can be nil in Swift. But, as I just said, photo is not an Optional. Therefore there is nothing to check. That is why Swift stops you when you try to perform such a check.
So, what to do? I have two possible suggestions:
My actual recommendation for solving this is that you do type photo as a UIImage? and set it initially to nil (actually, it is implicitly nil from the start). Now you can check for nil to see whether an actual image has been assigned to it.
But keep in mind that you then will have to remember to unwrap photo when you want to use it for anything! (You could instead type photo as an implicitly unwrapped optional, UIImage!, to avoid that constant unwrapping, but I am not as keen on that idea.)
An alternative possibility would be to examine the size of the image. If it is zero size, it is probably an empty image in the original sense of your question!
i am trying to get an imageview by passing image in Swift. But i got an error.
func setImageViewForImage(image:UIImage) -> UIImageView?{
var image_pmc:UIImage = image
var imageView_pmc:UIImageView = UIImageView(image: image_pmc)
imageView_pmc.frame = CGRectMake(self.frame.size.width/2 - image_pmc.size.width/2, 10, image_pmc.size.width, image_pmc.size.height)
return imageView_pmc
}
function call:
var imgView = setImageViewForImage(response) //error
//response is UIImage
The error is because your response variable is of AnyObject? type, which cannot be implicitly cast to UIImage, and however, being an optional, it must be unwrapped before passing to setImageViewForImage (which expects a non optional).
You should use optional binding combined with optional downcast as follows:
if let response = response as? UIImage {
myClass.setImageViewForImage(response)
}
where myClass is an instance of the class containing the setImageViewForImage method - presumably an instance of (a subclass of) UIView.
Note that I am making the assumption that response actually contains an UIImage - its name makes me think it's something you receive from a REST API call or similar.
Note that avoiding the optional binding and using explicit cast:
myClass.setImageViewForImage(response as UIImage)
is not safe because if response is nil or it doesn't contain a UIImage instance, the app will most likely crash.